* @copyright (c) 2009-2016 Open Classifieds Team * @license GPL v3 */ // Sanity check, install should only be checked from index.php defined('SYSPATH') or exit('Install must be loaded from within index.php!'); /** * Class with install functions helper */ class install{ /** * * Software install settings * @var string */ const VERSION = '4.0.0'; /** * default locale/language of the install * @var string */ public static $locale = 'en_UK'; /** * suggested URL with folder were to install * @var string */ public static $url = NULL; /** * suggested folder were to install * @var string */ public static $folder = NULL; /** * message to notify * @var string */ public static $msg = ''; /** * installation error messages here * @var string */ public static $error_msg = ''; /** * used to hash the password and set in the * @var string */ public static $hash_key = ''; /** * initializes the install class and process * @return void */ public static function initialize() { //Gets language to use in the install self::$locale = core::request('LANGUAGE', core::get_browser_favorite_language()); //start translations install::gettext_init(self::$locale); // Try to guess installation URL // Check whether we are using HTTPS or not if (isset($_SERVER['HTTPS'])) { if ('on' == strtolower($_SERVER['HTTPS']) OR '1' == $_SERVER['HTTPS']) { self::$url = 'https://'.$_SERVER['SERVER_NAME']; } } elseif (isset($_SERVER['SERVER_PORT']) AND ('443' == $_SERVER['SERVER_PORT'])) { self::$url = 'https://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT']; } else { self::$url = 'http://'.$_SERVER['SERVER_NAME']; if ($_SERVER['SERVER_PORT'] != '80') self::$url = self::$url.':'.$_SERVER['SERVER_PORT']; } //getting the folder, erasing the index self::$folder = str_replace('/index.php','', $_SERVER['SCRIPT_NAME']).'/'; self::$url .=self::$folder; } /** * checks that your hosting has everything that needs to have * @return array */ public static function requirements() { /** * mod rewrite check */ $mod_result = ((function_exists('apache_get_modules') AND in_array('mod_rewrite',apache_get_modules())) OR (getenv('HTTP_MOD_REWRITE')=='On') OR (strpos(@shell_exec('/usr/local/apache/bin/apachectl -l'), 'mod_rewrite') !== FALSE) OR (isset($_SERVER['IIS_UrlRewriteModule']))); $mod_msg = ($mod_result)?NULL:'Can not check if mod_rewrite is installed, probably everything is fine. Try to proceed with the installation anyway ;)'; /** * all the install checks */ return array( 'Write DIR' =>array('message' => 'Can\'t write to the current directory. Please fix this by giving the webserver user write access to the directory.', 'mandatory' => TRUE, 'result' => (is_writable(DOCROOT)) ), 'images' =>array('message' => 'The '.DOCROOT.'images/ directory is not writable.', 'mandatory' => TRUE, 'result' => is_writable(DOCROOT.'images') ), 'themes' =>array('message' => 'The '.DOCROOT.'themes/ directory is not writable.', 'mandatory' => TRUE, 'result' => is_writable(DOCROOT.'themes') ), 'cache' =>array('message' => 'The '.APPPATH.'cache/ directory is not writable.', 'mandatory' => TRUE, 'result' => (is_dir(APPPATH) AND is_dir(APPPATH.'cache') AND is_writable(APPPATH.'cache')) ), 'logs' =>array('message' => 'The '.APPPATH.'logs/ directory is not writable.', 'mandatory' => TRUE, 'result' => (is_dir(APPPATH) AND is_dir(APPPATH.'logs') AND is_writable(APPPATH.'logs')) ), 'SYS' =>array('message' => 'The configured '.SYSPATH.' directory does not exist or does not contain required files.', 'mandatory' => TRUE, 'result' => (is_dir(SYSPATH) AND is_file(SYSPATH.'classes/Kohana'.EXT)) ), 'APP' =>array('message' => 'The configured '.APPPATH.' directory does not exist or does not contain required files.', 'mandatory' => TRUE, 'result' => (is_dir(APPPATH) AND is_file(APPPATH.'bootstrap'.EXT)) ), 'PHP' =>array('message' => 'PHP 7.1 or newer required, this version is '. PHP_VERSION, 'mandatory' => TRUE, 'result' => version_compare(PHP_VERSION, '7.1', '>=') ), 'mod_rewrite'=>array('message' => $mod_msg, 'mandatory' => FALSE, 'result' => $mod_result ), 'Short Tag' =>array('message' => 'short_open_tag must be enabled in your php.ini.', 'mandatory' => TRUE, 'result' => (bool) ini_get('short_open_tag') ), 'Safe Mode' =>array('message' => 'SPL is either not loaded or not compiled in.', 'mandatory' => TRUE, 'result' => (function_exists('spl_autoload_register')) ), 'Reflection'=>array('message' => 'PHP reflection is either not loaded or not compiled in.', 'mandatory' => TRUE, 'result' => (class_exists('ReflectionClass')) ), 'Filters' =>array('message' => 'The filter extension is either not loaded or not compiled in.', 'mandatory' => TRUE, 'result' => (function_exists('filter_list')) ), 'Iconv' =>array('message' => 'The iconv extension is not loaded.', 'mandatory' => TRUE, 'result' => (extension_loaded('iconv')) ), 'Mbstring' =>array('message' => 'The mbstring extension is not loaded.', 'mandatory' => TRUE, 'result' => (extension_loaded('mbstring')) ), 'CType' =>array('message' => 'The ctype extension is not enabled.', 'mandatory' => TRUE, 'result' => (function_exists('ctype_digit')) ), 'URI' =>array('message' => 'Neither $_SERVER[\'REQUEST_URI\'], $_SERVER[\'PHP_SELF\'], or $_SERVER[\'PATH_INFO\'] is available.', 'mandatory' => TRUE, 'result' => (isset($_SERVER['REQUEST_URI']) OR isset($_SERVER['PHP_SELF']) OR isset($_SERVER['PATH_INFO'])) ), 'cUrl' =>array('message' => 'Install requires the cURL extension for the Request_Client_External class.', 'mandatory' => TRUE, 'result' => (extension_loaded('curl')) ), 'OpenSSL' =>array('message' => 'Install requires the OpenSSL for PHP versions 7.2 or above.', 'mandatory' => FALSE, 'result' => (extension_loaded('openssl')) ), 'Imagick' =>array('message' => 'Install requires the Imagick class', 'mandatory' => TRUE, 'result' => (class_exists('Imagick')) ), 'MySQL' =>array('message' => 'Install requires the MySQLi extension to support MySQL databases.', 'mandatory' => TRUE, 'result' => (function_exists('mysqli_connect')) ), 'ZipArchive' =>array('message' => 'PHP module zip not installed. You will need this to auto update the software.', 'mandatory' => FALSE, 'result' => class_exists('ZipArchive') ), 'SoapClient' =>array('message' => 'Install requires the SoapClient class.', 'mandatory' => FALSE, 'result' => class_exists('SoapClient') ), ); } /** * checks from requirements if its compatible or not. Also fills the msg variable * @return boolean */ public static function is_compatible() { self::$msg = array(); $compatible = TRUE; foreach (install::requirements() as $name => $values) { if ($values['mandatory'] == TRUE AND $values['result'] == FALSE) $compatible = FALSE; if ($values['result'] == FALSE) self::$msg[] = $values['message']; } return $compatible; } /** * includes a view file */ public static function view($file) { include_once 'views/'.$file.EXT; } /** * get phpinfo clean in a string * @return strin */ public static function phpinfo() { ob_start(); @phpinfo(); $phpinfo = ob_get_contents(); ob_end_clean(); //strip the body html return preg_replace('%^.*(.*).*$%ms', '$1', $phpinfo); } /** * loads gettexts or droppin * @param string $locale * @param string $domain * @param string $charset */ private static function gettext_init($locale,$domain = 'messages',$charset = 'utf8') { /** * check if gettext exists if not uses gettext dropin */ $locale_res = setlocale(LC_ALL, $locale); if ( !function_exists('_') OR $locale_res===FALSE OR empty($locale_res) ) { /** * gettext override * v 1.0.11 * https://launchpad.net/php-gettext/ * We load php-gettext here since Kohana_I18n tries to create the function __() function when we extend it. * PHP-gettext already does this. */ include APPPATH.'vendor/php-gettext/gettext.inc'; T_setlocale(LC_ALL, $locale); T_bindtextdomain($domain,DOCROOT.'languages'); T_bind_textdomain_codeset($domain, $charset); T_textdomain($domain); } /** * gettext exists using fallback in case locale doesn't exists */ else { bindtextdomain($domain,DOCROOT.'languages'); bind_textdomain_codeset($domain, $charset); textdomain($domain); function __($msgid) { return _($msgid); } } } /** * return HTML select for the timezones * @param string $select_name * @param string $selected * @return string */ public static function get_select_timezones($select_name='TIMEZONE', $selected=NULL) { if ($selected=='UTC') $selected='Europe/London'; $timezones = core::get_timezones(); $sel = ''; return $sel; } /** * replaces in a file * @param string $orig_file * @param array $search * @param array $replace * @return bool */ public static function replace_file($orig_file,$search, $replace,$to_file = NULL) { if ($to_file === NULL) $to_file = $orig_file; //check file is writable // if (is_writable($to_file)) // { //read file content $content = file_get_contents($orig_file); //replace fields $content = str_replace($search, $replace, $content); //save file return core::write_file($to_file,$content); // } // return FALSE; } /** * installs the software * @return [type] [description] */ public static function execute() { $error_msg = ''; $install = TRUE; /////////////////////////////////////////////////////// //check DB connection $link = @mysqli_connect(core::request('DB_HOST'), core::request('DB_USER'), core::request('DB_PASS')); if (!$link) { $error_msg = __('Cannot connect to server').' '. core::request('DB_HOST'); $install = FALSE; } if ($link AND $install === TRUE) { if (core::request('DB_NAME')) { //they selected to create the DB if (core::request('DB_CREATE')) @mysqli_query($link,"CREATE DATABASE IF NOT EXISTS `".core::request('DB_NAME')."`"); $dbcheck = @mysqli_select_db($link,core::request('DB_NAME')); if (!$dbcheck) { $error_msg.= __('Database name').': ' . mysqli_error($link); $install = FALSE; } } else { $error_msg.= '

'.__('No database name was given').'. '.__('Available databases').':

'; $db_list = @mysqli_query($link,'SHOW DATABASES'); $error_msg.= '
';
                if (!$db_list)
                {
                    $error_msg.= __('Invalid query'). ':
' . mysqli_error($link); } else { while ($row = mysqli_fetch_row($db_list)) { $error_msg.= $row['Database'] . '
'; } } $error_msg.= '
'; $install = FALSE; } } //clean prefix $TABLE_PREFIX = core::slug(core::request('TABLE_PREFIX')); //save DB config/database.php if ($install === TRUE) { $_POST['TABLE_PREFIX'] = $TABLE_PREFIX; $_GET['TABLE_PREFIX'] = $TABLE_PREFIX; $search = array('[DB_HOST]', '[DB_USER]','[DB_PASS]','[DB_NAME]','[TABLE_PREFIX]','[DB_CHARSET]'); $replace = array(core::request('DB_HOST'), core::request('DB_USER'), core::request('DB_PASS'),core::request('DB_NAME'),$TABLE_PREFIX,core::request('DB_CHARSET')); $install = install::replace_file(INSTALLROOT.'samples/database.php',$search,$replace,APPPATH.'config/database.php'); if (!$install === TRUE) $error_msg = __('Problem saving '.APPPATH.'config/database.php'); } //install DB if ($install === TRUE) { //check if has key is posted if not generate self::$hash_key = ((core::request('HASH_KEY')!='')?core::request('HASH_KEY'): core::generate_password() ); //check if DB was already installed, I use content since is the last table to be created $installed = (mysqli_num_rows(mysqli_query($link,"SHOW TABLES LIKE '".$TABLE_PREFIX."content'"))==1) ? TRUE:FALSE; if ($installed===FALSE)//if was installed do not launch the SQL. include INSTALLROOT.'samples/install.sql'.EXT; } /////////////////////////////////////////////////////// //AUTH config if ($install === TRUE) { $search = array('[HASH_KEY]', '[COOKIE_SALT]'); $replace = array(self::$hash_key,self::$hash_key,self::$hash_key); $install = install::replace_file(INSTALLROOT.'samples/auth.php',$search,$replace,APPPATH.'config/auth.php'); if (!$install === TRUE) $error_msg = __('Problem saving '.APPPATH.'config/auth.php'); } /////////////////////////////////////////////////////// //create robots.txt if ($install === TRUE) { $search = array('[SITE_URL]', '[SITE_FOLDER]'); $replace = array(core::request('SITE_URL'), core::request('SITE_FOLDER')); $install = install::replace_file(INSTALLROOT.'samples/robots.txt',$search,$replace,DOCROOT.'robots.txt'); if (!$install === TRUE) $error_msg = __('Problem saving '.DOCROOT.'robots.txt'); } /////////////////////////////////////////////////////// //create htaccess if ($install === TRUE) { $search = array('[SITE_FOLDER]'); $replace = array(core::request('SITE_FOLDER')); $install = install::replace_file(INSTALLROOT.'samples/example.htaccess',$search,$replace,DOCROOT.'.htaccess'); if (!$install === TRUE) $error_msg = __('Problem saving '.DOCROOT.'.htaccess'); } /////////////////////////////////////////////////////// //all good! if ($install === TRUE) { core::delete(INSTALLROOT.'install.lock'); //core::delete(INSTALLROOT);//prevents from performing a new install } //not succeded :( delete all the tables with that prefix elseif($link!=FALSE) { $table_list = @mysqli_query($link,"SHOW TABLES LIKE '".$TABLE_PREFIX."%'"); if ($table_list !== FALSE) while ($row = mysqli_fetch_assoc($table_list)) @mysqli_query($link,"DROP TABLE ".$row[0]); } self::$error_msg = $error_msg; return $install; } } class core{ /** * generates passwords, used for the auth hash keys etc.. * @param integer $length * @return string */ public static function generate_password ($length = 16) { // define possible characters $possible = '23456789@%$*abcdefghjkmnpqrstuvwxyz'; $possible_length = strlen($possible)-1; // add random characters to $password until $length is reached $password = ''; for ($i=0; $i <$length ; $i++) { // pick a random character from the possible ones $password .= substr($possible, mt_rand(0, $possible_length), 1); } return $password; } /** * Parse Accept-Language HTTP header to detect user's language(s) * and get the most favorite one * * Adapted from * @link http://www.thefutureoftheweb.com/blog/use-accept-language-header * @param string $lang default language to return in case of any * @return NULL|string favorite user's language * */ public static function get_browser_favorite_language($lang = 'en_US') { if ( ! isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) return $lang; // break up string into pieces (languages and q factors) preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); if ( ! count($lang_parse[1])) return $lang; // create a list of languages in the form 'es' => 0.8 $langs = array_combine($lang_parse[1], $lang_parse[4]); // set default to 1 for languages without a specified q factor foreach ($langs as $lang => $q) if ($q === '') $langs[$lang] = 1; arsort($langs, SORT_NUMERIC); // sort list based on q factor. higher first reset($langs); $lang = strtolower(key($langs)); // Gotcha ! the 1st top favorite language // when necessary, convert 'es' to 'es_ES' // so scandir("languages") will match and gettext_init below can seamlessly load its stuff if (strlen($lang) == 2) $lang .= '_'.strtoupper($lang); return str_replace('-', '_', $lang); } /** * gets the offset of a date * @param string $offset * @return string */ public static function format_offset($offset) { $hours = $offset / 3600; $remainder = $offset % 3600; $sign = $hours > 0 ? '+' : '-'; $hour = (int) abs($hours); $minutes = (int) abs($remainder / 60); if ($hour == 0 AND $minutes == 0) { $sign = ' '; } return $sign . str_pad($hour, 2, '0', STR_PAD_LEFT) .':'. str_pad($minutes,2, '0'); } /** * returns timezones ins a more friendly array way, ex Madrid [+1:00] * @return array */ public static function get_timezones() { if (method_exists('DateTimeZone','listIdentifiers')) { $utc = new DateTimeZone('UTC'); $dt = new DateTime('now', $utc); $timezones = array(); $timezone_identifiers = DateTimeZone::listIdentifiers(); foreach( $timezone_identifiers as $value ) { $current_tz = new DateTimeZone($value); $offset = $current_tz->getOffset($dt); if ( preg_match( '/^(America|Antartica|Africa|Arctic|Asia|Atlantic|Australia|Europe|Indian|Pacific)\//', $value ) ) { $ex=explode('/',$value);//obtain continent,city $city = isset($ex[2])? $ex[1].' - '.$ex[2]:$ex[1];//in case a timezone has more than one $timezones[$ex[0]][$value] = $city.' ['.core::format_offset($offset).']'; } } return $timezones; } else//old php version { return FALSE; } } /** * cleans an string of spaces etc * @param string $s * @return string clean */ public static function slug($s) { // everything to lower and no spaces begin or end $s = strip_tags(strtolower(trim($s))); // adding - for spaces and union characters $find = array(' ', '&', '+', '-',',','.',';'); $s = str_replace ($find, '', $s); //return the friendly s return $s; } /** * write to file * @param $filename fullpath file name * @param $content * @return boolean */ public static function write_file($filename,$content) { $file = fopen($filename, 'w'); if ($file) {//able to create the file fwrite($file, $content); fclose($file); return TRUE; } return FALSE; } /** * deletes file or directory recursevely * @param string $file * @return void */ public static function delete($file) { if (is_dir($file)) { $objects = scandir($file); foreach ($objects as $object) { if ($object != '.' AND $object != '..') { if (is_dir($file.'/'.$object)) core::delete($file.'/'.$object); else unlink($file.'/'.$object); } } reset($objects); @rmdir($file); } elseif(is_file($file)) unlink($file); } /** * rss reader * @param string $url * @return array */ public static function rss($url) { $rss = @simplexml_load_file($url); if($rss == FALSE OR ! isset($rss->channel->item)) return array(); return $rss->channel->item; } /** * shortcut for the query method $_GET * @param [type] $key [description] * @param [type] $default [description] * @return [type] [description] */ public static function get($key,$default=NULL) { return (isset($_GET[$key]))?$_GET[$key]:$default; } /** * shortcut for $_POST[] * @param [type] $key [description] * @param [type] $default [description] * @return [type] [description] */ public static function post($key,$default=NULL) { return (isset($_POST[$key]))?$_POST[$key]:$default; } /** * shortcut to get or post * @param [type] $key [description] * @param [type] $default [description] * @return [type] [description] */ public static function request($key,$default=NULL) { return (core::post($key)!==NULL)?core::post($key):core::get($key,$default); } }