/**
  * Set a requested theme and template as default for the site.
  */
 public function setdefault()
 {
     $request = $this->getPageRequest();
     $view = $this->getView();
     $themename = $this->getPageRequest()->getParameter(0);
     $template = $this->getPageRequest()->getParameter('template');
     // If the browser prefers JSON data, send that.
     if ($request->prefersContentType(View::CTYPE_JSON)) {
         $view->contenttype = View::CTYPE_JSON;
     }
     // Validate the theme name
     if (!\Theme\validate_theme_name($themename)) {
         \Core\set_message('Invalid theme requested', 'error');
         \Core\go_back();
     }
     $theme = ThemeHandler::GetTheme($themename);
     if ($template) {
         // The template itself can be ignored.
         if (!\Theme\validate_template_name($themename, $template)) {
             \Core\set_message('Invalid template requested', 'error');
             \Core\go_back();
         }
     } else {
         // and the default one is used otherwise.
         $allskins = $theme->getSkins();
         $template = $allskins[0]['file'];
     }
     if (Core::IsComponentAvailable('multisite') && MultiSiteHelper::GetCurrentSiteID()) {
         $config_default = ConfigHandler::GetConfig('/theme/default_template');
         $config_selected = ConfigHandler::GetConfig('/theme/selected');
         if ($config_default->get('overrideable') == 0) {
             // It's a child site and the admin never gave them permission to change default themes!
             \Core\set_message('Unable to set the default template on a child site, please ensure that the "/theme/default_template" config is set to be overrideable!', 'error');
             \Core\go_back();
         }
         if ($config_selected->get('overrideable') == 0) {
             // It's a child site and the admin never gave them permission to change default themes!
             \Core\set_message('Unable to set the selected theme on a child site, please ensure that the "/theme/selected" config is set to be overrideable!', 'error');
             \Core\go_back();
         }
     }
     if ($request->isPost()) {
         if ($themename != ConfigHandler::Get('/theme/selected')) {
             // The theme changed, change the admin skin too!
             ConfigHandler::Set('/theme/default_admin_template', $template);
             // And the email skin.
             ConfigHandler::Set('/theme/default_email_template', '');
         }
         ConfigHandler::Set('/theme/default_template', $template);
         ConfigHandler::Set('/theme/selected', $themename);
         // reinstall theme and zee assets
         $t = ThemeHandler::GetTheme();
         if (($change = $t->reinstall(0)) !== false) {
             SystemLogModel::LogInfoEvent('/updater/theme/reinstall', 'Theme ' . $t->getName() . ' reinstalled successfully', implode("\n", $change));
         }
         \Core\set_message('Updated default theme', 'success');
         \Core\redirect('/theme');
     }
     $view->assign('theme', $themename);
     $view->assign('template', $template);
 }
Example #2
0
 /**
  * Execute the actual cron for the requested type.
  *
  * @param string $cron Cron type to execute.
  *
  * @return CronLogModel
  * @throws Exception
  */
 private function _performcron($cron)
 {
     switch ($cron) {
         case '1-minute':
         case '5-minute':
         case '15-minute':
         case '30-minute':
         case 'hourly':
         case '2-hour':
         case '3-hour':
         case '6-hour':
         case '12-hour':
         case 'daily':
         case 'weekly':
         case 'monthly':
             break;
         default:
             throw new Exception('Unsupported cron type: [' . $cron . ']');
     }
     if (!ConfigHandler::Get('/cron/enabled')) {
         $msg = 'Cron execution is globally disabled via the site configuration, not executing cron!';
         SystemLogModel::LogInfoEvent('/cron/' . $cron, $msg);
         // It needs to return something.
         $log = new CronLogModel();
         $log->set('status', 'fail');
         return $log;
     }
     // First, check and see if there's one that's still running.
     $runninglogs = CronLogModel::Find(array('cron' => $cron, 'status' => 'running'));
     if (sizeof($runninglogs)) {
         foreach ($runninglogs as $log) {
             /** @var $log CronLogModel */
             $log->set('status', 'fail');
             $log->set('log', $log->get('log') . "\n------------\nTIMED OUT!");
             $log->save();
         }
     }
     // Start recording.
     $log = new CronLogModel();
     $log->set('cron', $cron);
     $log->set('status', 'running');
     $log->set('memory', memory_get_usage());
     $log->set('ip', REMOTE_IP);
     $log->save();
     $start = microtime(true) * 1000;
     $sep = '==========================================' . "\n";
     $contents = "Starting cron execution for {$cron}\n{$sep}";
     // This uses the hook system, but will be slightly different than most things.
     $overallresult = true;
     $hook = HookHandler::GetHook('/cron/' . $cron);
     $hookcount = 0;
     $hooksuccesses = 0;
     if ($hook) {
         if ($hook->getBindingCount()) {
             $hookcount = $hook->getBindingCount();
             $bindings = $hook->getBindings();
             foreach ($bindings as $b) {
                 $contents .= sprintf("\nExecuting Binding %s...\n", $b['call']);
                 // Since these systems will just be writing to STDOUT, I'll need to capture that.
                 try {
                     ob_start();
                     $execution = $hook->callBinding($b, array());
                     $executiondata = ob_get_clean();
                 } catch (Exception $e) {
                     $execution = false;
                     $executiondata = 'EXCEPTION: ' . $e->getMessage() . ob_get_clean();
                 }
                 if ($executiondata == '' && $execution) {
                     $contents .= "Cron executed successfully with no output\n";
                     ++$hooksuccesses;
                 } elseif ($execution) {
                     $contents .= $executiondata . "\n";
                     ++$hooksuccesses;
                 } else {
                     $contents .= $executiondata . "\n!!FAILED\n";
                     $overallresult = false;
                 }
             }
         } else {
             $contents = 'No bindings located for requested cron';
             $overallresult = true;
         }
     } else {
         $contents = 'Invalid hook requested: ' . $cron;
         $overallresult = false;
     }
     // Just in case the contents are returning html... (they should be plain text).
     // Replace the most common line endings with things that make sense for plain text.
     // This is to ensure that all the available scenarios are met and saved/displayed without extra whitespace.
     //
     // Since some systems will provide plain text (easy!), windows/os9 line endings,
     // HTML (br and br/), and formatted HTML (br + \n).
     $contents = str_ireplace(["\r\n<br>", "\r\n<br/>", "\r\n<br />", "\n<br>", "\n<br/>", "\n<br />", "<br>", "<br/>", "<br />", "\r\n", "\r"], "\n", $contents);
     // Save the results.
     $log->set('completed', Time::GetCurrentGMT());
     $log->set('duration', microtime(true) * 1000 - $start);
     $log->set('log', $contents);
     $log->set('status', $overallresult ? 'pass' : 'fail');
     $log->save();
     // Make a copy of this in the system log too if applicable.
     // This time is listed in ms
     $time = microtime(true) * 1000 - $start;
     // 0.01 = 10 ns
     // 1    = 1 ms
     // 1000 = 1 second
     if ($time < 1) {
         // TIME is less than 1, which means it executed faster than 1ms, display in nanoseconds.
         $time = round($time, 4) * 1000 . ' ns';
     } elseif ($time < 1000) {
         // TIME is less than 1000, which means it executed faster than 1 second, display in milliseconds.
         $time = round($time, 0) . ' ms';
     } else {
         // TIME is at least 1 second or longer... Display in minutes:seconds, (no need to display 1.453 seconds!)
         // First, convert the milliseconds to seconds; they are more manageable for what I need to do.
         // This will change time from 12345(ms) to 13(seconds)
         $time = ceil($time / 1000);
         $minutes = floor($time / 60);
         $seconds = $time - $minutes * 60;
         if ($minutes > 0) {
             $time = $minutes . 'm ' . str_pad($seconds, 2, '0', STR_PAD_LEFT) . 's';
         } else {
             $time = $seconds . ' seconds';
         }
     }
     if ($hookcount > 0) {
         $msg = 'Cron ' . $cron . ' completed in ' . $time . '.  ' . $hooksuccesses . ' out of ' . $hookcount . ' hooks called successfully.';
         SystemLogModel::LogInfoEvent('/cron/' . $cron, $msg, $contents);
     }
     // Just to notify the calling function.
     return $log;
 }
Example #3
0
<?php

/**
 * Upgrade file for Core 2.8.0 to 3.0.0 (Security 1.2.0 to 1.3.0)
 *
 * The security model has been migrated into Core as of 3.0, so this script
 * migrates the data from the security component into Core.
 * 
 * @author Charlie Powell <*****@*****.**>
 * @date 20131229.2033
 */
// Handle conversion of the security entries to system log entries.
// These are now contained in that table.
$secfac = new ModelFactory('SecurityLogModel');
$stream = new \Core\Datamodel\DatasetStream($secfac->getDataset());
while ($row = $stream->getRecord()) {
    //$model = SystemLogModel::Construct($row['id']);
    $model = new SystemLogModel($row['id']);
    // Standard keys
    $model->setFromArray(['datetime' => $row['datetime'], 'session_id' => $row['session_id'], 'user_id' => $row['user_id'], 'ip_addr' => $row['ip_addr'], 'useragent' => $row['useragent'], 'code' => $row['action'], 'affected_user_id' => $row['affected_user_id'], 'message' => $row['details']]);
    $model->set('type', $row['status'] == 'fail' ? 'security' : 'info');
    $model->save();
}
Example #4
0
	/**
	 * Set this component as enabled in the database.
	 */
	public function enable(){
		// If it's not installed already, it can't be disabled!
		if($this->isEnabled()) return false;

		$c = new ComponentModel($this->_name);
		$c->set('enabled', true);
		$c->save();
		$this->_enabled = true;

		$changed = array();

		$change = $this->_parseUserConfigs();
		if ($change !== false) $changed = array_merge($changed, $change);

		$change = $this->_parsePages();
		if ($change !== false) $changed = array_merge($changed, $change);

		// Do this when I actually have widgets to test.
		//$change = $this->_parseWidgets();
		//if ($change !== false) $changed = array_merge($changed, $change);

		if(sizeof($changed)){
			SystemLogModel::LogInfoEvent('/updater/component/enable', 'Component ' . $this->getName() . ' enabled successfully!', implode("\n", $changed));
		}

		// Ensure that the core component cache is purged too!
		\Core\Cache::Delete('core-components');

		return (sizeof($changed)) ? $changed : false;
	}
	/**
	 * Helper function called by the *_install views.
	 *
	 * @param $type
	 * @param $name
	 * @param $version
	 */
	private function _performInstall($type, $name, $version){
		$view = $this->getView();
		$req  = $this->getPageRequest();

		$dryrun  = $req->getParameter('dryrun');
		$verbose = $req->getParameter('verbose');
		$nl      = "<br/>\n";

		// For standard calls, this is a json-only page.
		// verbose runs are html however.
		if($verbose){
			$view->contenttype = View::CTYPE_HTML;
			$view->mode = View::MODE_NOOUTPUT;
		}
		else{
			$view->contenttype = View::CTYPE_JSON;
		}

		// This is a post-only page!
		if(!$req->isPost()){
			$view->error = View::ERROR_BADREQUEST;
			return;
		}

		$return = UpdaterHelper::PerformInstall($type, $name, $version, $dryrun, $verbose);


		// If it's not a dry run, record a log of this action!
		if(!$dryrun){
			if($return['status']){
				$logmsg = 'Installation of ' . $type . ' ' . $name . ' ' . $version . ' succeeded!' . "\n" . $return['message'];
				if(isset($return['changes'])){
					foreach($return['changes'] as $change){
						$logmsg .= "\n" . $change;
					}
				}
				$logstatus = 'success';
			}
			else{
				$logmsg = 'Installation of ' . $type . ' ' . $name . ' ' . $version . ' failed due to' . "\n" . $return['message'];
				$logstatus = 'fail';
			}

			SystemLogModel::LogSecurityEvent(
				'/updater/installation',
				$logmsg
			);
		}


		if($verbose){
			if(!$return['status']){
				echo $nl . '[===========  RESULTS  ===========]' . $nl;
				echo '[ERROR] - ' . $return['message'] . $nl;
			}
			else{
				echo $nl . '[===========  RESULTS  ===========]' . $nl;
				if(isset($return['changes'])){
					foreach($return['changes'] as $change){
						echo '[INFO] - ' . $change . $nl;
					}
				}
				echo '[SUCCESS] - Performed all operations successfully!' . $nl;
			}
			echo '<div id="results" style="display:none;" status="' . $return['status'] . '">' . $return['message'] . '</div>';
		}
		else{
			$view->jsondata = $return;
		}

	}
Example #6
0
 /**
  * "Upgrade" (aka) Install this theme and its assets.
  *
  * Alias of install()
  *
  * @return false | array
  * @throws \InstallerException
  */
 public function upgrade()
 {
     //if(!$this->isInstalled()) return false;
     $changes = $this->_performInstall();
     if (is_array($changes) && sizeof($changes)) {
         \SystemLogModel::LogInfoEvent('/updater/theme/upgrade', 'Theme ' . $this->getName() . ' installed successfully!', implode("\n", $changes));
     }
     return $changes;
 }
Example #7
0
	/**
	 * Function that is fired off on page load.
	 * This checks if a form was submitted and that form was present in the SESSION.
	 *
	 * @return null
	 */
	public static function CheckSavedSessionData() {
		// This needs to ignore the /form/savetemporary.ajax page!
		// This is a custom page that's meant to intercept all POST submissions.
		if(preg_match('#^/form/(.*)\.ajax$#', REL_REQUEST_PATH)) return;

		// There has to be data in the session.
		$forms = \Core\Session::Get('FormData/*');

		$formid = (isset($_REQUEST['___formid'])) ? $_REQUEST['___formid'] : false;
		$form   = false;

		foreach ($forms as $k => $v) {
			// If the object isn't a valid object after unserializing...
			if (!($el = unserialize($v))) {
				\Core\Session::UnsetKey('FormData/' . $k);
				continue;
			}

			// Check the expires time
			if ($el->get('expires') <= Time::GetCurrent()) {
				\Core\Session::UnsetKey('FormData/' . $k);
				continue;
			}

			if ($k == $formid) {
				// Remember this for after all the checks have finished.
				$form = $el;
			}
		}

		// No form found... simple enough
		if (!$form) return;

		// Otherwise
		/** @var $form Form */

		// Ensure the submission types match up.
		if (strtoupper($form->get('method')) != $_SERVER['REQUEST_METHOD']) {
			\Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_TYPE_DOES_NOT_MATCH');
			return;
		}

		// Ensure the REFERRER and original URL match up.
		if($_SERVER['HTTP_REFERER'] != $form->originalurl){
			// @todo This is reported to be causing issues with production sites.
			//       If found true, this check may need to be removed / refactored.
			//\Core\set_message('Form submission referrer does not match, please try your submission again.', 'error');
			SystemLogModel::LogInfoEvent(
				'Form Referrer Mismatch',
				'Form referrer does not match!  Submitted: [' . $_SERVER['HTTP_REFERER'] . '] Expected: [' . $form->originalurl . ']'
			);
			//return;
		}

		// Run though each element submitted and try to validate it.
		if (strtoupper($form->get('method')) == 'POST') $src =& $_POST;
		else $src =& $_GET;

		$form->loadFrom($src);

		// Try to load the form from that form.  That will call all of the model's validation logic
		// and will throw exceptions if it doesn't.
		try{
			$form->getModel();

			// Still good?
			if (!$form->hasError()){
				$status = call_user_func($form->get('callsmethod'), $form);
			}
			else{
				$status = false;
			}
		}
		catch(ModelValidationException $e){
			\Core\set_message($e->getMessage(), 'error');
			$status = false;
		}
		catch(GeneralValidationException $e){
			\Core\set_message($e->getMessage(), 'error');
			$status = false;
		}
		catch(Exception $e){
			if(DEVELOPMENT_MODE){
				// Developers get the full message
				\Core\set_message($e->getMessage(), 'error');
			}
			else{
				// While users of production-enabled sites get a friendlier message.
				\Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION');
			}
			Core\ErrorManagement\exception_handler($e);
			$status = false;
		}

		// The form was submitted.  Set its persistent flag to true so that whatever may be listening for it can retrieve the user's values.
		$form->persistent = true;

		// Regardless, bundle this form back into the session so the controller can use it if needed.
		\Core\Session::Set('FormData/' . $formid, serialize($form));

		// Fail statuses.
		if ($status === false) return;
		if ($status === null) return;

		// Guess it's not false and not null... must be good then.

		// @todo Handle an internal save procedure for "special" groups such as pageinsertables and what not.

		// Cleanup
		\Core\Session::UnsetKey('FormData/' . $formid);


		if ($status === 'die'){
			// If it's set to die, simply exit the script without outputting anything.
			exit;
		}
		elseif($status === 'back'){
			if($form->referrer && $form->referrer != REL_REQUEST_PATH){
				// Go back to the original form's referrer.
				\Core\redirect($form->referrer);
			}
			else{
				// Use Core to guess which page to redirect back to, (not as reliable).
				\Core\go_back();
			}
		}
		elseif ($status === true){
			// If the return code is boolean true, it's a reload.
			\Core\reload();
		}
		elseif($status === REL_REQUEST_PATH || $status === CUR_CALL){
			// If the page returned the same page as the current url, force a reload, (as redirect will ignore it)
			\Core\reload();
		}
		else{
			// Anything else gets sent to the redirect system.
			\core\redirect($status);
		}
	}
 /**
  * Check the user's IP in the blacklist and see if it's found.
  *
  * If it is and has a high enough submission rate, (in a 24 hour period), then block the user completely and immediately.
  */
 public static function CheckIP()
 {
     $record = \sfsBlacklistModel::Construct(REMOTE_IP);
     // It's not in there, YAY!
     if (!$record->exists()) {
         return;
     }
     // Is the submission score high enough?
     $highscore = 100;
     if ($record->get('submissions') > $highscore) {
         // YOU can haz good party tiem nau
         \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $highscore . ' submissions to sfs in a 24 hour period.');
         die('IP Blocked due to high spam score');
     }
     // Submissions listed, but not exceedingly high?
     $warnlevel = 5;
     if ($record->get('submissions') > $warnlevel) {
         if (\Core\Session::Get('security_antispam_allowed') === null) {
             $html = '<html><body>';
             $html .= '<!-- You smell of spam.... are you sure you didn\'t come from a can?-->';
             if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['happyfuntime']) && \Core\Session::Get('happyfuntimecheck')) {
                 // It's an attempt!
                 if ($_POST['happyfuntime'] == \Core\Session::Get('happyfuntimecheck')) {
                     \SystemLogModel::LogSecurityEvent('/security/unblocked', 'User successfully answered an anti-bot math question, unblocking.');
                     \Core\Session::Set('security_antispam_allowed', true);
                 } else {
                     \SystemLogModel::LogSecurityEvent('/security/captchafailed', 'User attempted, but failed in answering an anti-bot math question.');
                     $html .= '<b>NOPE!</b>';
                 }
             }
             \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to over ' . $warnlevel . ' submissions to sfs in a 24 hour period.');
             $random1 = rand(4, 6) * 2;
             $random2 = rand(1, 3) * 2;
             $random3 = rand(1, 2);
             switch ($random3) {
                 case 1:
                     $result = $random1 / $random2;
                     $operation = 'divided by';
                     break;
                 case 2:
                     $result = $random1 * $random2;
                     $operation = 'multiplied by';
                     break;
             }
             \Core\Session::Set('happyfuntimecheck', $result);
             switch ($random2) {
                 case 1:
                     $random2 = 'oNe';
                     break;
                 case 2:
                     $random2 = 'Tw0';
                     break;
                 case 3:
                     $random2 = 'ThRe';
                     break;
                 case 4:
                     $random2 = 'Foor';
                     break;
                 case 5:
                     $random2 = 'fIve';
                     break;
                 case 6:
                     $random2 = 'Siix';
                     break;
             }
             $html .= '<form method="POST"><p>What is ' . $random1 . ' ' . $operation . ' ' . $random2 . '?</p><input type="text" name="happyfuntime" size="3"/><input type="submit" value="GO"/></form></body></html>';
             die($html);
         }
     }
 }
Example #9
0
	/**
	 * The actual Core registration page.
	 *
	 * This renders all the user's configurable options at registration.
	 */
	public function register2(){
		$view    = $this->getView();
		$request = $this->getPageRequest();
		$manager = \Core\user()->checkAccess('p:/user/users/manage'); // Current user an admin?

		// Anonymous users should have access to this if it's allow public.
		if(!\Core\user()->exists() && !ConfigHandler::Get('/user/register/allowpublic')){
			return View::ERROR_BADREQUEST;
		}

		// Authenticated users must check the permission to manage users.
		if(\Core\user()->exists() && !$manager){
			return View::ERROR_ACCESSDENIED;
		}

		/** @var NonceModel $nonce */
		$nonce = NonceModel::Construct($request->getParameter(0));
		if(!$nonce->isValid()){
			\Core\set_message('Invalid nonce token, please try again.', 'error');
			\Core\go_back();
		}
		$nonce->decryptData();
		$data = $nonce->get('data');

		if(!isset($data['user']) || !($data['user'] instanceof UserModel)){
			if(DEVELOPMENT_MODE){
				\Core\set_message('Your nonce does not include a "user" key.  Please ensure that this is set to a non-existent UserModel object!', 'error');
			}
			else{
				\Core\set_message('Invalid login type, please try again later.', 'error');
			}
			\Core\go_back();
		}

		/** @var UserModel $user */
		$user = $data['user'];

		$form = \Core\User\Helper::GetForm($user);
		
		// If the total number of form elements here are only 2, then only the user object and submit button are present.
		// Instead of showing the form, auto-submit to that destination.
		if(sizeof($form->getElements()) <= 2){
			$user->setDefaultGroups();
			$user->setDefaultMetaFields();
			$user->setDefaultActiveStatuses();
			$user->generateNewApiKey();
			$user->save();

			// User created... make a log of this!
			\SystemLogModel::LogSecurityEvent('/user/register', 'User registration successful', null, $user->get('id'));

			// Send a thank you for registering email to the user.
			try{
				$user->sendWelcomeEmail();
			}
			catch(\Exception $e){
				\Core\ErrorManagement\exception_handler($e);
				\Core\set_message('t:MESSAGE_ERROR_CANNOT_SEND_WELCOME_EMAIL');
			}

			// "login" this user if not already logged in.
			if(!\Core\user()->exists()){
				if($user->get('active')){
					$user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT));
					$user->save();
					\Core\Session::SetUser($user);
				}
				\Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT');

				if(($overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'))){
					// Allow an external script to override the redirecting URL.
					$url = $overrideurl;
				}
				elseif($form->getElementValue('redirect')){
					// The preferred default redirect method.
					// This is set from /user/register2, which is in turn passed in, (hopefully), by the original callee registration page.
					$url = $form->getElementValue('redirect');
				}
				elseif(strpos(REL_REQUEST_PATH, '/user/register') === 0){
					// If the user came from the registration page, get the page before that.
					$url = '/';
				}
				else{
					// else the registration link is now on the same page as the 403 handler.
					$url = REL_REQUEST_PATH;
				}

				\Core\redirect($url);
			}
			// It was created administratively; redirect there instead.
			else{
				\Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT');
				\Core\redirect('/user/admin');
			}
		}
		
		$form->addElement('hidden', ['name' => 'redirect', 'value' => $data['redirect']]);

		$view->title = 'Complete Registration';
		$view->assign('form', $form);
	}
 /**
  * View to accept and process the FB login post.
  *
  * This will redirect to the registration page if the user doesn't exist,
  * will throw an error and display a link to enable FB if it's not enabled already,
  * or will simply log the user in via Facebook and sync his/her settings.
  */
 public function login()
 {
     $view = $this->getView();
     $request = $this->getPageRequest();
     $view->ssl = true;
     $view->record = false;
     $auths = \Core\User\Helper::GetEnabledAuthDrivers();
     if (!isset($auths['facebook'])) {
         // Facebook isn't enabled, simply redirect to the home page.
         \Core\redirect('/');
     }
     if (!FACEBOOK_APP_ID) {
         \Core\redirect('/');
     }
     if (!FACEBOOK_APP_SECRET) {
         \Core\redirect('/');
     }
     if (!$request->isPost()) {
         return View::ERROR_BADREQUEST;
     }
     $facebook = new Facebook(['appId' => FACEBOOK_APP_ID, 'secret' => FACEBOOK_APP_SECRET]);
     // Did the user submit the facebook login request?
     if (isset($_POST['login-method']) && $_POST['login-method'] == 'facebook' && $_POST['access-token']) {
         try {
             $facebook->setAccessToken($_POST['access-token']);
             /** @var int $fbid The user ID from facebook */
             $fbid = $facebook->getUser();
             /** @var array $user_profile The array of user data from Facebook */
             $user_profile = $facebook->api('/me');
         } catch (Exception $e) {
             \Core\set_message($e->getMessage(), 'error');
             \Core\go_back();
             return null;
         }
         /** @var \UserModel|null $user */
         $user = UserModel::Find(['email' => $user_profile['email']], 1);
         if (!$user) {
             if (ConfigHandler::Get('/user/register/allowpublic')) {
                 // If public registration is enabled, then redirect the user to the registration page to complete their registration.
                 $user = new UserModel();
                 $user->set('email', $user_profile['email']);
                 $user->enableAuthDriver('facebook');
                 $user->disableAuthDriver('datastore');
                 /** @var \Facebook\UserAuth $auth */
                 $auth = $user->getAuthDriver('facebook');
                 $auth->syncUser($_POST['access-token']);
                 // Otherwise, w00t!  Record this user into a nonce and forward to step 2 of registration.
                 $nonce = NonceModel::Generate('20 minutes', null, ['user' => $user, 'redirect' => $_POST['redirect']]);
                 \Core\redirect('/user/register2/' . $nonce);
             } else {
                 // Log this as a login attempt!
                 $logmsg = 'Failed Login (Facebook). Email not registered' . "\n" . 'Email: ' . $user_profile['email'] . "\n";
                 \SystemLogModel::LogSecurityEvent('/user/login', $logmsg);
                 \Core\set_message('Your Facebook email (' . $user_profile['email'] . ') does not appear to be registered on this site.', 'error');
                 \Core\go_back();
                 return null;
             }
         } elseif (!$user->get('active')) {
             // The model provides a quick cut-off for active/inactive users.
             // This is the control managed with in the admin.
             $logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n";
             \SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $user->get('id'));
             \Core\set_message('Your account is not active yet.', 'error');
             \Core\go_back();
             return null;
         }
         try {
             /** @var \Facebook\UserAuth $auth */
             $auth = $user->getAuthDriver('facebook');
         } catch (Exception $e) {
             \Core\set_message('Your account does not have Facebook logins enabled!  <a href="' . \Core\resolve_link('/facebook/enable') . '">Do you want to enable Facebook?</a>', 'error');
             \Core\go_back();
             return null;
         }
         if (!$user->isActive()) {
             \Core\set_message('Your account is not active!', 'error');
             \Core\go_back();
             return null;
         }
         // Well yay the user is available and authencation driver is ready!
         $auth->syncUser($_POST['access-token']);
         if ($_POST['redirect']) {
             // The page was set via client-side javascript on the login page.
             // This is the most reliable option.
             $url = $_POST['redirect'];
         } elseif (REL_REQUEST_PATH == '/facebook/login') {
             // If the user came from the registration page, get the page before that.
             $url = '/';
         } else {
             // else the registration link is now on the same page as the 403 handler.
             $url = REL_REQUEST_PATH;
         }
         // Well, record this too!
         \SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via Facebook)', null, $user->get('id'));
         // yay...
         $user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT));
         $user->save();
         \Core\Session::SetUser($user);
         // Allow an external script to override the redirecting URL.
         $overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl');
         if ($overrideurl) {
             $url = $overrideurl;
         }
         \Core\redirect($url);
     } else {
         \Core\go_back();
     }
 }
Example #11
0
	/**
	 * Validate the verification email, part 2 of confirmation.
	 *
	 * @param string $nonce
	 * @param string $signature
	 *
	 * @return bool|string
	 */
	public static function ValidateVerificationResponse($nonce, $signature) {
		/** @var \NonceModel $nonce */
		$nonce = \NonceModel::Construct($nonce);

		if(!$nonce->isValid()){
			\SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key (Invalid NONCE)', null);
			return 'Invalid nonce provided!';
		}

		// Now is where the real fun begins.

		$nonce->decryptData();

		$data = $nonce->get('data');

		/** @var \UserModel $user */
		$user   = \UserModel::Construct($data['user']);
		$gpg    = new \Core\GPG\GPG();
		$key    = $data['key'];
		$pubKey = $gpg->getKey($key);

		try{
			$sig = $gpg->verifyDataSignature($signature, $data['sentence']);
		}
		catch(\Exception $e){
			\SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key ' . $key, null, $user->get('id'));
			return 'Invalid signature';
		}

		$fpr = str_replace(' ', '', $sig->fingerprint); // Trim spaces.
		if($key != $fpr && $key != $sig->keyID){
			// They must match!
			\SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'FAILED to verify key ' . $key, null, $user->get('id'));
			return 'Invalid signature';
		}

		// Otherwise?
		$user->enableAuthDriver('gpg');
		$user->set('gpgauth_pubkey', $fpr);

		// Was there a photo attached to this public key?
		if(sizeof($pubKey->getPhotos()) > 0){
			$p = $pubKey->getPhotos();
			// I just want the first.
			/** @var \Core\Filestore\File $p */
			$p = $p[0];

			$localFile = \Core\Filestore\Factory::File('public/user/avatar/' . $pubKey->fingerprint . '.' . $p->getExtension());
			$p->copyTo($localFile);
			$user->set('avatar', $localFile->getFilename(false));
		}
		
		$user->save();

		$nonce->markUsed();

		\SystemLogModel::LogSecurityEvent('/user/gpg/verified', 'Verified key ' . $fpr, null, $user->get('id'));

		return true;
	}
Example #12
0
/**
 * Get the current user model that is logged in.
 *
 * To support legacy systems, this will also return the User object if it's available instead.
 * This support is for < 2.8.x Core installations and will be removed after some amount of time TBD.
 *
 * If no user systems are currently available, null is returned.
 *
 * @return \UserModel
 */
function user(){
	static $_CurrentUserAccount = null;

	if(!class_exists('\\UserModel')){
		return null;
	}

	if($_CurrentUserAccount !== null){
		// Cache this for the page load.
		return $_CurrentUserAccount;
	}

	if(isset($_SERVER['HTTP_X_CORE_AUTH_KEY'])){
		// Allow an auth key to be used to authentication the requested user instead!
		$user = \UserModel::Find(['apikey = ' . $_SERVER['HTTP_X_CORE_AUTH_KEY']], 1);
		if($user){
			$_CurrentUserAccount = $user;
		}
	}
	elseif(Session::Get('user') instanceof \UserModel){
		// There is a valid user account in the session!
		// But check if this user is forced to be resynced first.
		if(isset(Session::$Externals['user_forcesync'])){
			// A force sync was requested by something that modified the original UserModel object.
			// Keep the user logged in, but reload the data from the database.
			$_CurrentUserAccount = \UserModel::Construct(Session::Get('user')->get('id'));
			// And cache this updated user model back to the session.
			Session::Set('user', $_CurrentUserAccount);
			unset(Session::$Externals['user_forcesync']);
		}
		else{
			$_CurrentUserAccount = Session::Get('user');
		}
	}

	if($_CurrentUserAccount === null){
		// No valid user found.
		$_CurrentUserAccount = new \UserModel();
	}

	// If this is in multisite mode, blank out the access string cache too!
	// This is because siteA may have some groups, while siteB may have another.
	// We don't want a user going to a site they have full access to, hopping to another and having cached permissions!
	if(\Core::IsComponentAvailable('multisite') && class_exists('MultiSiteHelper') && \MultiSiteHelper::IsEnabled()){
		$_CurrentUserAccount->clearAccessStringCache();
	}

	// Did this user request sudo access for another user?
	if(Session::Get('user_sudo') !== null){
		$sudo = Session::Get('user_sudo');

		if($sudo instanceof \UserModel){
			// It's a valid user!

			if($_CurrentUserAccount->checkAccess('p:/user/users/sudo')){
				// This user can SUDO!
				// (only if the other user is < SA or current == SA).
				if($sudo->checkAccess('g:admin') && !$_CurrentUserAccount->checkAccess('g:admin')){
					Session::UnsetKey('user_sudo');
					\SystemLogModel::LogSecurityEvent('/user/sudo', 'Authorized but non-SA user requested sudo access to a system admin!', null, $sudo->get('id'));
				}
				else{
					// Ok, everything is good.
					// Remap the current user over to this sudo'd account!
					$_CurrentUserAccount = $sudo;
				}
			}
			else{
				// This user can NOT sudo!!!
				Session::UnsetKey('user_sudo');
				\SystemLogModel::LogSecurityEvent('/user/sudo', 'Unauthorized user requested sudo access to another user!', null, $sudo->get('id'));
			}
		}
		else{
			Session::UnsetKey('user_sudo');
		}
	}

	return $_CurrentUserAccount;
}
 /**
  * Handle the upload as a series of binary streams.
  * @return array
  */
 private function _doStream()
 {
     // Read INPUT and write it directly to a temporary file based on the requested filename.
     //var_dump($_SERVER); die();
     $name = substr($_SERVER['HTTP_CONTENT_DISPOSITION'], 22, -1);
     $file = array('name' => $name, 'size' => 0, 'remaining' => 0, 'type' => null, 'url' => '', 'thumbnail_url' => '', 'error' => '');
     //$finalsize = $_SERVER['HTTP_X_FILE_SIZE'];
     if (isset($_SERVER['HTTP_CONTENT_RANGE'])) {
         $contentrange = explode('/', $_SERVER['HTTP_CONTENT_RANGE']);
         $finalsize = $contentrange[1];
     } else {
         $finalsize = $_SERVER['CONTENT_LENGTH'];
     }
     $incomingsize = $_SERVER['CONTENT_LENGTH'];
     // Just used to prevent multiple pageloads from appending to the same file should something happen.
     $datestamp = isset($_SERVER['HTTP_X_UPLOAD_TIME']) ? $_SERVER['HTTP_X_UPLOAD_TIME'] : 0;
     $tmpfile = TMP_DIR . md5($this->_formelement->get('key') . $file['name'] . $datestamp) . '.part.dat';
     // Record the filesize before and after so I can confirm I got all the data the client sent.
     if (file_exists($tmpfile)) {
         $file['size'] = filesize($tmpfile);
     } else {
         $file['size'] = 0;
     }
     if (!is_writable(TMP_DIR)) {
         $file['error'] = 'Unable to write to temporary directory.';
         return array('files' => [$file]);
     }
     file_put_contents($tmpfile, file_get_contents('php://input'), FILE_APPEND);
     // And update the size.
     clearstatcache();
     $newsize = filesize($tmpfile);
     if ($newsize - $file['size'] != $incomingsize) {
         $file['error'] = 'Did not receive all data, unable to process upload';
         unlink($tmpfile);
         return array('files' => [$file]);
     }
     $file['size'] = $newsize;
     $file['remaining'] = $finalsize - $file['size'];
     // Is the file upload complete?
     if ($file['size'] == $finalsize) {
         // Source
         $f = \Core\Filestore\Factory::File($tmpfile);
         // Destination
         // Make sure the filename is sanitized.
         $newbasename = \Core\str_to_url(urldecode($file['name']), true);
         $nf = \Core\Filestore\Factory::File($this->_formelement->get('basedir') . $newbasename);
         if (!$nf->isWritable()) {
             $file['error'] = 'File destination is not writable.';
             \SystemLogModel::LogErrorEvent('warning', dirname($nf->getFilename()) . ' does not appear to be writable!');
             return array('files' => [$file]);
         }
         $file['type'] = $f->getMimetype();
         // do NOT copy the contents over until the accept check has been ran!
         // Now that I have a file object, (in the temp filesystem still), I should validate the filetype
         // to see if the developer wanted a strict "accept" type to be requested.
         // If present, I'll have something to run through and see if the file matches.
         // I need the destination now because I need to full filename if an extension is requested in the accept.
         if ($this->_formelement->get('accept')) {
             $acceptcheck = \Core\check_file_mimetype($this->_formelement->get('accept'), $f->getMimetype(), $nf->getExtension());
             // Now that all the mimetypes have run through, I can see if one matched.
             if ($acceptcheck != '') {
                 $file['error'] = $acceptcheck;
                 unlink($tmpfile);
                 return array('files' => [$file]);
             }
         }
         // Now all the checks should be completed and I can safely copy the file away from the temporary filesystem.
         $f->copyTo($nf);
         unlink($tmpfile);
         // And now all the file's attributes will be visible.
         $file['name'] = $nf->getBaseFilename();
         $file['url'] = $nf->getURL();
         $file['thumbnail_url'] = $nf->getPreviewURL('50x50');
     }
     return array('files' => [$file]);
 }
	/**
	 * Form Handler for logging in.
	 *
	 * @static
	 *
	 * @param \Form $form
	 *
	 * @return bool|null|string
	 */
	public static function LoginHandler(\Form $form){
		/** @var \FormElement $e */
		$e = $form->getElement('email');
		/** @var \FormElement $p */
		$p = $form->getElement('pass');


		/** @var \UserModel $u */
		$u = \UserModel::Find(array('email' => $e->get('value')), 1);

		if(!$u){
			// Log this as a login attempt!
			$logmsg = 'Failed Login. Email not registered' . "\n" . 'Email: ' . $e->get('value') . "\n";
			\SystemLogModel::LogSecurityEvent('/user/login', $logmsg);
			$e->setError('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND');
			return false;
		}

		if($u->get('active') == 0){
			// The model provides a quick cut-off for active/inactive users.
			// This is the control managed with in the admin.
			$logmsg = 'Failed Login. User tried to login before account activation' . "\n" . 'User: '******'email') . "\n";
			\SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id'));
			$e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_NOT_ACTIVE');
			return false;
		}
		elseif($u->get('active') == -1){
			// The model provides a quick cut-off for active/inactive users.
			// This is the control managed with in the admin.
			$logmsg = 'Failed Login. User tried to login after account deactivation.' . "\n" . 'User: '******'email') . "\n";
			\SystemLogModel::LogSecurityEvent('/user/login', $logmsg, null, $u->get('id'));
			$e->setError('t:MESSAGE_ERROR_USER_LOGIN_ACCOUNT_DEACTIVATED');
			return false;
		}

		try{
			/** @var \Core\User\AuthDrivers\datastore $auth */
			$auth = $u->getAuthDriver('datastore');
		}
		catch(Exception $e){
			$e->setError('t:MESSAGE_ERROR_USER_LOGIN_PASSWORD_AUTH_DISABLED');
			return false;
		}


		// This is a special case if the password isn't set yet.
		// It can happen with imported users or if a password is invalidated.
		if($u->get('password') == ''){
			// Use the Nonce system to generate a one-time key with this user's data.
			$nonce = \NonceModel::Generate(
				'20 minutes',
				['type' => 'password-reset', 'user' => $u->get('id')]
			);

			$link = '/datastoreauth/forgotpassword?e=' . urlencode($u->get('email')) . '&n=' . $nonce;

			$email = new \Email();
			$email->setSubject('Initial Password Request');
			$email->to($u->get('email'));
			$email->assign('link', \Core\resolve_link($link));
			$email->assign('ip', REMOTE_IP);
			$email->templatename = 'emails/user/initialpassword.tpl';
			try{
				$email->send();
				\SystemLogModel::LogSecurityEvent('/user/initialpassword/send', 'Initial password request sent successfully', null, $u->get('id'));

				\Core\set_message('t:MESSAGE_INFO_USER_LOGIN_MUST_SET_NEW_PASSWORD_INSTRUCTIONS_HAVE_BEEN_EMAILED');
				return true;
			}
			catch(\Exception $e){
				\Core\ErrorManagement\exception_handler($e);
				\Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_MUST_SET_NEW_PASSWORD_UNABLE_TO_SEND_EMAIL');
				return false;
			}
		}


		if(!$auth->checkPassword($p->get('value'))){

			// Log this as a login attempt!
			$logmsg = 'Failed Login. Invalid password' . "\n" . 'Email: ' . $e->get('value') . "\n";
			\SystemLogModel::LogSecurityEvent('/user/login/failed_password', $logmsg, null, $u->get('id'));

			// Also, I want to look up and see how many login attempts there have been in the past couple minutes.
			// If there are too many, I need to start slowing the attempts.
			$time = new \CoreDateTime();
			$time->modify('-5 minutes');

			$securityfactory = new \ModelFactory('SystemLogModel');
			$securityfactory->where('code = /user/login/failed_password');
			$securityfactory->where('datetime > ' . $time->getFormatted(\Time::FORMAT_EPOCH, \Time::TIMEZONE_GMT));
			$securityfactory->where('ip_addr = ' . REMOTE_IP);

			$attempts = $securityfactory->count();
			if($attempts > 4){
				// Start slowing down the response.  This should help deter brute force attempts.
				// (x+((x-7)/4)^3)-4
				sleep( ($attempts+(($attempts-7)/4)^3)-4 );
				// This makes a nice little curve with the following delays:
				// 5th  attempt: 0.85
				// 6th  attempt: 2.05
				// 7th  attempt: 3.02
				// 8th  attempt: 4.05
				// 9th  attempt: 5.15
				// 10th attempt: 6.52
				// 11th attempt: 8.10
				// 12th attempt: 10.05
			}

			$e->setError('t:MESSAGE_ERROR_USER_LOGIN_INCORRECT_PASSWORD');
			$p->set('value', '');
			return false;
		}


		if($form->getElementValue('redirect')){
			// The page was set via client-side javascript on the login page.
			// This is the most reliable option.
			$url = $form->getElementValue('redirect');
		}
		elseif(REL_REQUEST_PATH == '/user/login'){
			// If the user came from the registration page, get the page before that.
			$url = $form->referrer;
		}
		else{
			// else the registration link is now on the same page as the 403 handler.
			$url = REL_REQUEST_PATH;
		}

		// Well, record this too!
		\SystemLogModel::LogSecurityEvent('/user/login', 'Login successful (via password)', null, $u->get('id'));

		// yay...
		$u->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT));
		$u->save();
		\Core\Session::SetUser($u);

		// Allow an external script to override the redirecting URL.
		$overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl');
		if($overrideurl){
			$url = $overrideurl;
		}

		return $url;
	}
Example #15
0
	/**
	 * Send the message
	 *
	 * @throws phpmailerException
	 * @return bool
	 */
	public function send() {
		$m = $this->getMailer();

		if(!\ConfigHandler::Get('/core/email/enable_sending')){
			// Allow a config option to disable sending entirely.
			SystemLogModel::LogInfoEvent('/email/disabled', 'Email sending is disabled, not sending email ' . $m->Subject . '!');
			return false;
		}

		if(\ConfigHandler::Get('/core/email/sandbox_to')){
			$to  = $m->getToAddresses();
			$cc  = $m->getCCAddresses();
			$bcc = $m->getBCCAddresses();
			$all = [];

			if(sizeof($to)){
				foreach($to as $e){
					$all[] = ['type' => 'To', 'email' => $e[0], 'name' => $e[1]];
				}
			}
			if(sizeof($cc)){
				foreach($cc as $e){
					$all[] = ['type' => 'CC', 'email' => $e[0], 'name' => $e[1]];
				}
			}
			if(sizeof($bcc)){
				foreach($bcc as $e){
					$all[] = ['type' => 'BCC', 'email' => $e[0], 'name' => $e[1]];
				}
			}

			foreach($all as $e){
				$m->AddCustomHeader('X-Original-' . $e['type'], ($e['name'] ? $e['name'] . ' <' . $e['email'] . '>' : $e['email']));
			}

			// Allow a config option to override the "To" address, useful for testing with production data.
			$m->ClearAllRecipients();
			$m->AddAddress(\ConfigHandler::Get('/core/email/sandbox_to'));
		}

		// Render out the body.  Will be either HTML or text...
		$body = $this->renderBody();

		// Wrap this body with the main email template if it's set.
		if($this->templatename && $this->_view){
			// This version includes HTML tags and all that.
			$m->Body = $body;
			$m->IsHTML(true);
			// Use markdown for conversion.
			// It produces better results that phpMailer's built-in system!
			$converter = new \HTMLToMD\Converter();

			// Manually strip out the head content.
			// This was throwing the converters for a loop and injecting weird characters!
			$body = preg_replace('#<head[^>]*?>.*</head>#ms', '', $body);

			$m->AltBody = $converter->convert($body);
		}
		elseif (strpos($body, '<html>') === false) {
			// Ensuring that the body is wrapped with <html> tags helps with spam checks with spamassassin.
			$m->MsgHTML('<html><body>' . $body . '</body></html>');
		}
		else{
			$m->MsgHTML($body);
		}

		if($this->_encryption){
			// Encrypt this message, (both HTML and Alt), and all attachments.
			// I need to request the full EML from phpMailer so I can encrypt everything.
			// Then, the body will be recreated after Send is called.
			$m->PreSend();
			$header = $m->CreateHeader();
			$body   = $m->CreateBody();
			$gpg    = new \Core\GPG\GPG();

			if($this->_encryption === true){
				// This is allowed for mutliple recipients!
				// This requires a little more overhead, as I need to lookup each recipient's user account
				// to retrieve their GPG key.
				$recipients = $m->getToAddresses();

				foreach($recipients as $dat){
					$email = $dat[0];
					$user = UserModel::Find(['email = ' . $email], 1);
					if(!$user){
						SystemLogModel::LogErrorEvent('/core/email/failed', 'Unable to locate GPG key for ' . $email . ', cannot send encrypted email to recipient!');
					}
					else{
						$key = $user->get('gpgauth_pubkey');
						if(!$key){
							SystemLogModel::LogErrorEvent('/core/email/failed', 'No GPG key uploaded for ' . $email . ', cannot send encrypted email to recipient!');
						}
						else{
							$enc = $gpg->encryptData($header . $body, $key);

							// Create a clone of the email object to send this data.
							/** @var PHPMailer $clone */
							$clone = clone $m;
							$clone->ClearAddresses();
							$clone->AddAddress($email);
							$clone->Body = $enc;
							$clone->AltBody = '';
							$clone->Send();
						}
					}
				}
				return true;
			}
			else{
				// Single recipient!
				$enc = $gpg->encryptData($header . $body, $this->_encryption);

				$m->Body = $enc;
				$m->AltBody = '';
				return $m->Send();
			}
		}

		return $m->Send();
	}
 /**
  * This will check and see how many 404 requests there have been recently.
  *
  * @return bool
  */
 public static function Check404Pages()
 {
     // How long back do I want to check the logs?
     $time = new DateTime();
     $time->modify('-30 seconds');
     $ds = Dataset::Init()->table('user_activity')->where(['status = 404', 'ip_addr = ' . REMOTE_IP, 'datetime > ' . $time->format('U')])->count()->execute();
     if ($ds->num_rows > 30) {
         // CHILL THAR F****R!
         $time->modify('+6 minutes');
         $blacklist = new \IpBlacklistModel();
         $blacklist->setFromArray(['ip_addr' => REMOTE_IP . '/24', 'expires' => $time->format('U'), 'message' => 'You have requested too many "404" pages recently, please go get some coffee and wait for a short bit.  If you are a bot and/or spammer, please bugger off.', 'comment' => '5-minute auto-ban for too many 404 requests in 30 seconds']);
         $blacklist->save();
         \SystemLogModel::LogSecurityEvent('/security/blocked', 'Blocking IP due to too many 404 requests in 30 seconds.');
         die($blacklist->get('message'));
     }
 }
Example #17
0
	/**
	 * Page to display full details of a system log, usually opened in an ajax dialog.
	 * 
	 * @return int
	 * @throws DMI_Exception
	 */
	public function log_details(){
		$view = $this->getView();
		$request = $this->getPageRequest();

		$view->mode = View::MODE_PAGEORAJAX;

		if(!\Core\user()->checkAccess('p:/core/systemlog/view')){
			return View::ERROR_ACCESSDENIED;
		}
		
		$log = SystemLogModel::Construct($request->getParameter(0));
		if(!$log->exists()){
			return View::ERROR_NOTFOUND;
		}

		$view->mastertemplate = 'admin';
		$view->addBreadcrumb('t:STRING_SYSTEM_LOG', '/admin/log');
		$view->title = 't:STRING_SYSTEM_LOG_DETAILS';
		$view->assign('entry', $log);
	}
 /**
  * Check permissions on the user and system and return either blank or a string containing the error.
  *
  * @param string $step
  *
  * @return array|null
  */
 private function _checkPermissions($step)
 {
     $error = null;
     if (!\ConfigHandler::Get('/package_repository/base_directory')) {
         // Check if the config is even set, can't proceed if it's not.
         trigger_error('The package repository does not appear to be setup yet.  Please browse to Configuration and the appropriate options.');
         return ['status' => View::ERROR_SERVERERROR, 'message' => 'The package repository is not setup on this server.'];
     }
     $dir = Factory::Directory(\ConfigHandler::Get('/package_repository/base_directory'));
     if (!$dir->exists()) {
         trigger_error($dir->getPath() . ' does not appear to exist!  Unable to browse repo.xml without it.');
         return ['status' => View::ERROR_SERVERERROR, 'message' => $dir->getPath() . ' does not seem to exist!'];
     } elseif (!$dir->isReadable()) {
         trigger_error($dir->getPath() . ' does not appear to be readable!  Unable to browse repo.xml without it.');
         return ['status' => View::ERROR_SERVERERROR, 'message' => $dir->getPath() . ' does not seem to be readable!'];
     }
     if (ConfigHandler::Get('/package_repository/is_private')) {
         // Lookup this license key, (or request one if not present).
         $valid = false;
         $autherror = 'Access to ' . SITENAME . ' (Package Repository) requires a license key and password.';
         if (isset($_SERVER['PHP_AUTH_PW']) && isset($_SERVER['PHP_AUTH_USER'])) {
             $user = $_SERVER['PHP_AUTH_USER'];
             $pw = $_SERVER['PHP_AUTH_PW'];
         } else {
             $user = $pw = null;
         }
         if ($user && $pw) {
             /** @var PackageRepositoryLicenseModel $license */
             $license = PackageRepositoryLicenseModel::Construct($user);
             $licvalid = $license->isValid($pw);
             if ($licvalid == 0) {
                 // Lock this license to the remote IP, if requested by the admin.
                 if (ConfigHandler::Get('/package_repository/auto_ip_restrict') && !$license->get('ip_restriction')) {
                     $license->set('ip_restriction', REMOTE_IP);
                     $license->save();
                 }
                 SystemLogModel::LogInfoEvent('/packagerepository/' . $step, '[' . $user . '] accessed repository successfully');
                 return null;
             } else {
                 if (($licvalid & PackageRepositoryLicenseModel::VALID_PASSWORD) == PackageRepositoryLicenseModel::VALID_PASSWORD) {
                     $autherror = '[' . $user . '] Invalid license password';
                     $status = View::ERROR_ACCESSDENIED;
                     SystemLogModel::LogSecurityEvent('/packagerepository/password_failure', $autherror);
                 }
                 if (($licvalid & PackageRepositoryLicenseModel::VALID_ACCESS) == PackageRepositoryLicenseModel::VALID_ACCESS) {
                     $autherror = '[' . $user . '] IP address not authorized';
                     $status = View::ERROR_ACCESSDENIED;
                     SystemLogModel::LogSecurityEvent('/packagerepository/ip_restriction', $autherror);
                 }
                 if (($licvalid & PackageRepositoryLicenseModel::VALID_EXPIRED) == PackageRepositoryLicenseModel::VALID_EXPIRED) {
                     $autherror = '[' . $user . '] License provided has expired, please request a new one.';
                     $status = View::ERROR_GONE;
                     SystemLogModel::LogSecurityEvent('/packagerepository/expired_license', $autherror);
                 }
                 if (($licvalid & PackageRepositoryLicenseModel::VALID_INVALID) == PackageRepositoryLicenseModel::VALID_INVALID) {
                     $autherror = '[' . $user . '] License does not exist';
                     $status = View::ERROR_EXPECTATIONFAILED;
                     SystemLogModel::LogSecurityEvent('/packagerepository/invalid_license', $autherror);
                 }
                 return ['status' => $status, 'message' => $autherror];
             }
         }
         if (!$valid) {
             header('WWW-Authenticate: Basic realm="' . SITENAME . ' (Package Repository)"');
             header('HTTP/1.0 401 Unauthorized');
             echo $autherror;
             exit;
         }
     } else {
         SystemLogModel::LogInfoEvent('/packagerepository/' . $step, '[anonymous connection] accessed repository successfully');
         return null;
     }
 }
Example #19
0
	/**
	 * Load all the components in the system, replacement for the Core.
	 * @throws CoreException
	 */
	private function _loadComponents() {
		// cannot reload components.
		if ($this->_components) return null;

		$this->_components = array();
		$this->_libraries  = array();
		$tempcomponents    = false;
		Core\Utilities\Logger\write_debug('Starting loading of component metadata');

		// If the site is in DEVELOPMENT mode, component caching would probably be a bad idea; ie: the developer probably wants
		// those component files loaded everytime.
		if(DEVELOPMENT_MODE){
			$enablecache = false;
		}
		else{
			$enablecache = true;
		}

		// Is there a cache of elements available?  This is a primary system cache that greatly increases performance,
		// since it will no longer have to run through each component.xml file to register each one.
		if($enablecache){
			Core\Utilities\Logger\write_debug('Checking core-components cache');
			// Try to load up the cached components and check them first.
			$tempcomponents = \Core\Cache::Get('core-components', (3600 * 24));

			if($tempcomponents !== false){
				// Cached components only need to be loaded.
				foreach ($tempcomponents as $c) {
					try {
						$c->load();
					}
					catch (Exception $e) {
						// Don't completely bail out here, just invalidate the cache and continue on.
						\Core\Cache::Delete('core-components');
						$tempcomponents = false;
					}
				}
			}
		}


		if(!$enablecache || $tempcomponents == false){
			\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Scanning for component.xml files manually');
			Core\Utilities\Logger\write_debug('Scanning for component.xml files manually');

			// Core is first, (obviously)
			$tempcomponents['core'] = ComponentFactory::Load(ROOT_PDIR . 'core/component.xml');
			Core\Utilities\Logger\write_debug('Core component loaded');

			// First, build my cache of components, regardless if the component is installed or not.
			$dh = opendir(ROOT_PDIR . 'components');
			if (!$dh) throw new CoreException('Unable to open directory [' . ROOT_PDIR . 'components/] for reading.');

			// This will read through every directory in 'components', which is
			// where all components in the system are installed to.
			while (($file = readdir($dh)) !== false) {
				// skip hidden directories.
				if ($file{0} == '.') continue;

				// skip non-directories
				if (!is_dir(ROOT_PDIR . 'components/' . $file)) continue;

				// Skip directories that do not have a readable component.xml file.
				if (!is_readable(ROOT_PDIR . 'components/' . $file . '/component.xml')) continue;

				//Core\Utilities\Logger\write_debug(' * Loading component ' . $file);
				$c = ComponentFactory::Load(ROOT_PDIR . 'components/' . $file . '/component.xml');
				Core\Utilities\Logger\write_debug('Opened component ' . $file);

				// All further operations are case insensitive.
				// The original call to Component needs to be case sensitive because it sets the filename to pull.
				$file = strtolower($file);

				// If the component was flagged as invalid.. just skip to the next one.
				if (!$c->isValid()) {
					if (DEVELOPMENT_MODE) {
						\Core\set_message('Component ' . $c->getName() . ' appears to be invalid.');
					}
					continue;
				}


				$tempcomponents[$file] = $c;
				unset($c);
			}
			closedir($dh);
			\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Component XML files scanned');

			// Now I probably could actually load the components!

			foreach ($tempcomponents as $c) {
				/** @var Component_2_1 $c */
				try {
					// Load some of the data in the class so that it's available in the cached version.
					// This is because the component 2.1 has built-in caching for many of the XML requests.
					// by calling them once, that lookup data is cached in that component, which in turn gets
					// copied to the cache version here!
					$c->load();
					$c->getClassList();
					$c->getViewSearchDir();
					$c->getSmartyPluginDirectory();
					$c->getWidgetList();
				}
				catch (Exception $e) {
					var_dump($e);
					die();
				}
			}

			// Cache this list!
			if($enablecache){
				Core\Utilities\Logger\write_debug(' * Caching core-components for next pass');
				\Core\Cache::Set('core-components', $tempcomponents, (3600 * 24));
			}
		}

		$list = $tempcomponents;

		\Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Component metadata loaded, starting registration');
		Core\Utilities\Logger\write_debug(' * Component metadata loaded, starting registration');

		// The core component at a minimum needs to be loaded and registered.
		//		$this->_registerComponent($list['core']);
		//		$this->_components['core']->loadFiles();
		//		unset($list['core']);

		// Now that I have a list of components available, copy them into a list of 
		//	components that are installed.

		do {
			$size = sizeof($list);
			foreach ($list as $n => $c) {
				/** @var $c Component_2_1 */

				// Disabled components don't get recognized.
				if($c->isInstalled() && !$c->isEnabled()){
					// But they do get sent to the disabled list!
					$this->_componentsDisabled[$n] = $c;

					unset($list[$n]);
					continue;
				}

				// Clear out the temporary class list
				$this->_tmpclasses = [];

				// If it's loaded, register it and remove it from the list!
				if ($c->isInstalled() && $c->isLoadable() && $c->loadFiles()) {

					try{
						// Allow for on-the-fly package upgrading regardless of DEV mode or not.
						if ($c->needsUpdated()) {

							// Load this component's classes in case an upgrade operation requires one.
							// This allows a component to be loaded partially without completely being loaded.
							$this->_tmpclasses = $c->getClassList();

							// Lock the site first!
							// This is because some upgrade procedures take a long time to upgrade.
							file_put_contents(TMP_DIR . 'lock.message', 'Core Plus is being upgraded, please try again in a minute. ');
							$c->upgrade();
							unlink(TMP_DIR . 'lock.message');
						}
					}
					catch(Exception $e){
						SystemLogModel::LogErrorEvent('/core/component/failedupgrade', 'Ignoring component [' . $n . '] due to an error during upgrading!', $e->getMessage());

						unlink(TMP_DIR . 'lock.message');
						//$c->disable();
						$this->_componentsDisabled[$n] = $c;
						unset($list[$n]);
						continue;
					}

					try{
						$this->_components[$n] = $c;
						$this->_registerComponent($c);
						$c->loadSupplementalModels();
					}
					catch(Exception $e){
						SystemLogModel::LogErrorEvent('/core/component/failedregister', 'Ignoring component [' . $n . '] due to an error during registration!', $e->getMessage());

						//$c->disable();
						$this->_componentsDisabled[$n] = $c;
						unset($list[$n]);
						continue;
					}

					unset($list[$n]);
					continue;
				}


				// Allow for on-the-fly package upgrading regardless of DEV mode or not.
				// Guess this is needed for the loadFiles part...
				if ($c->isInstalled() && $c->needsUpdated() && $c->isLoadable()) {
					// Lock the site first!
					// This is because some upgrade procedures take a long time to upgrade.
					file_put_contents(TMP_DIR . 'lock.message', 'Core Plus is being upgraded, please try again in a minute. ');

					$c->upgrade();
					$c->loadFiles();
					$this->_components[$n] = $c;
					$this->_registerComponent($c);
					unlink(TMP_DIR . 'lock.message');

					unset($list[$n]);
					continue;
				}

				// Allow packages to be auto-installed if in DEV mode.
				// If DEV mode is not enabled, just install the new component, do not enable it.
				if (!$c->isInstalled() && $c->isLoadable()) {
					// Load this component's classes in case an install operation requires one.
					// This allows a component to be loaded partially without completely being loaded.
					$this->_tmpclasses = $c->getClassList();

					// w00t
					$c->install();
					// BLAH, until I fix the disabled-packages-not-viewable bug...
					$c->enable();
					$c->loadFiles();
					$this->_components[$n] = $c;
					$this->_registerComponent($c);

					/*
					if(!DEVELOPMENT_MODE){
						$c->disable();
					}
					else{
						$c->enable();
						$c->loadFiles();
						$this->_components[$n] = $c;
						$this->_registerComponent($c);
					}
					*/
					unset($list[$n]);
					continue;
				}
			}
		}
		while ($size > 0 && ($size != sizeof($list)));

		// If dev mode is enabled, display a list of components installed but not loadable.

		foreach ($list as $n => $c) {

			//$this->_components[$n] = $c;
			$this->_componentsDisabled[$n] = $c;

			// Ignore anything with the execmode different, those should be minor notices for debugging if anything.
			if ($c->error & Component_2_1::ERROR_WRONGEXECMODE) continue;

			if (DEVELOPMENT_MODE) {
				SystemLogModel::LogErrorEvent('/core/component/missingrequirement', 'Could not load installed component ' . $n . ' due to requirement failed.', $c->getErrors());
			}
		}

		// Don't forget to load the themes too!
		if(class_exists('ThemeHandler')){
			foreach(ThemeHandler::GetAllThemes() as $theme){
				/** @var $theme Theme */
				$theme->load();
			}
		}

		// Lastly, make sure that the template path cache is updated!
		if(class_exists('\\Core\\Templates\\Template')){
			\Core\Templates\Template::RequeryPaths();	
		}
	}
Example #20
0
	/**
	 * Form handler for the rest of the user system, (auth handler has already been executed).
	 *
	 * @param \Form $form
	 *
	 * @return bool|string
	 */
	public static function RegisterHandler(\Form $form){

		///////       VALIDATION     \\\\\\\\

		// All other validation can be done from the model.
		// All set calls will throw a ModelValidationException if the validation fails.
		try{
			/** @var \UserModel $user */
			$user = $form->getElement('user')->get('value');

			// setFromForm will handle all attributes and custom values.
			$user->setFromForm($form);
		}
		catch(\ModelValidationException $e){
			// Make a note of this!
			\SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage());

			\Core\set_message($e->getMessage(), 'error');
			return false;
		}
		catch(\Exception $e){
			// Make a note of this!
			\SystemLogModel::LogSecurityEvent('/user/register', $e->getMessage());

			if(DEVELOPMENT_MODE){
				\Core\set_message($e->getMessage(), 'error');
			}
			else{
				\Core\set_message('t:MESSAGE_ERROR_FORM_SUBMISSION_UNHANDLED_EXCEPTION');
			}

			return false;
		}

		if( \Core\user()->checkAccess('g:admin') ) {
			$active = ($form->getElementValue('active') === "on" ? 1 : 0);
			$user->set('active', $active);
		}
		else {
			$user->setDefaultActiveStatuses();
		}
		
		$user->setDefaultGroups();
		$user->setDefaultMetaFields();
		$user->generateNewApiKey();
		$user->save();

		// User created... make a log of this!
		\SystemLogModel::LogSecurityEvent('/user/register', 'User registration successful', null, $user->get('id'));

		// Send a thank you for registering email to the user.
		try{
			$user->sendWelcomeEmail();
		}
		catch(\Exception $e){
			\Core\ErrorManagement\exception_handler($e);
			\Core\set_message('t:MESSAGE_ERROR_CANNOT_SEND_WELCOME_EMAIL');
		}

		// "login" this user if not already logged in.
		if(!\Core\user()->exists()){
			if($user->get('active')){
				$user->set('last_login', \CoreDateTime::Now('U', \Time::TIMEZONE_GMT));
				$user->save();
				Session::SetUser($user);
			}
			\Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT');

			if(($overrideurl = \HookHandler::DispatchHook('/user/postlogin/getredirecturl'))){
				// Allow an external script to override the redirecting URL.
				$url = $overrideurl;
			}
			elseif($form->getElementValue('redirect')){
				// The preferred default redirect method.
				// This is set from /user/register2, which is in turn passed in, (hopefully), by the original callee registration page.
				$url = $form->getElementValue('redirect');
			}
			elseif(strpos(REL_REQUEST_PATH, '/user/register') === 0){
				// If the user came from the registration page, get the page before that.
				$url = '/';
			}
			else{
				// else the registration link is now on the same page as the 403 handler.
				$url = REL_REQUEST_PATH;
			}

			return $url;
		}
		// It was created administratively; redirect there instead.
		else{
			\Core\set_message('t:MESSAGE_SUCCESS_CREATED_USER_ACCOUNT');
			return '/user/admin';
		}
	}
	public function preview() {
		$view = $this->getView();
		$request = $this->getPageRequest();

		// This is designed to only return data!
		$view->mode = View::MODE_NOOUTPUT;
		// And it's going to be an image of some sorts.
		// This will get overridden by the actual image displaying method.
		$view->contenttype = 'image/png';
		// And it shouldn't be recorded in navigation.
		$view->record = false;

		// The filename should be something like files/public/blah... or files/assets/foo...
		// This will get fed into the core system and checked internally.
		$filename = $request->getParameter('f');


		// Was there a resize-to dimension requested?
		$d = $request->getParameter('d');


		// The inbound filename must be in the format of base64:[b64data].
		if (strpos($filename, 'base64:') !== 0) {
			if(DEVELOPMENT_MODE){
				error_log('Invalid request made for /file/preview!  Expecting: [base64:*b64data*], Received: [' . $filename . ']');
			}

			$file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png');
			$file->displayPreview($d);
			return;
		}

		// This preview ONLY supports assets and public files!
		// This is a security precaution.
		$base = base64_decode(substr($filename, 7));

		if(!(strpos($base, 'public/') === 0 || strpos($base, 'assets/') === 0 || strpos($base, 'asset/') === 0)){
			SystemLogModel::LogSecurityEvent('/file/preview', 'Invalid file requested: ' . $base);

			if(DEVELOPMENT_MODE){
				error_log('Invalid request made for /file/preview!  Expecting: [public/* or asset[s]/*], Received: [' . $base . ']');
			}

			$file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png');
			$file->displayPreview($d);
			return;
		}

		$file = \Core\Filestore\Factory::File($filename);
		if(!$file->exists()){
			if(DEVELOPMENT_MODE){
				error_log('File not found for /file/preview!  Looking For: [' . $file->getFilename('') . ' (' . $filename . ') ]');
			}

			$file = \Core\Filestore\Factory::File('assets/images/mimetypes/notfound.png');
			$file->displayPreview($d);
			return;
		}

		// And this will render it to the browser.
		$file->displayPreview($d);
	}
	public function finalize(){
		$subject = 'Import completed in ' . $this->_profiler->getTimeFormatted();
		$bits = [];
		if($this->success > 0){
			$bits[] = 'Success: ' . $this->success;
		}
		if($this->skipped > 0){
			$bits[] = 'Skipped: ' . $this->skipped;
		}
		if($this->error > 0){
			$bits[] = 'Errors: ' . $this->error;
		}
		if($this->duplicate > 0){
			$bits[] = 'Duplicates: ' . $this->duplicate;
		}
		$subject .= ' ' . implode(', ', $bits);

		\SystemLogModel::LogInfoEvent($this->_title, $subject, $this->_profiler->getEventTimesFormatted());
	}
 /**
  * Shortcut function to record a new security log entry.
  *
  * Return the log entry that's created.
  *
  * @param string      $action
  * @param null|string $status
  * @param null|int    $affecteduser
  * @param null|string $details
  *
  * @return null
  */
 public static function Log($action, $status = null, $affecteduser = null, $details = null)
 {
     // The security log has been removed!
     trigger_error('SecurityLogModel::Log has been deprecated, please use SystemLogModel::LogSecurityEvent() instead.', E_USER_DEPRECATED);
     SystemLogModel::LogSecurityEvent($action, $details, null, $affecteduser);
 }
 public function wpadmin()
 {
     $view = $this->getView();
     $request = $this->getPageRequest();
     if ($request->isPost()) {
         // Did they actually try to submit this form?......  silly bot ;)
         SystemLogModel::LogSecurityEvent('/wp-admin Honeypot POST', 'POST submission to /wp-admin detected!', print_r($_POST, true));
         $expireback = new CoreDateTime();
         $expireback->modify('+2 days');
         $block = IpBlacklistModel::Find(['ip_addr = ' . REMOTE_IP . '/32'], 1);
         if (!$block) {
             $block = new IpBlacklistModel();
             $block->set('ip_addr', REMOTE_IP . '/32');
         }
         $block->setFromArray(['expires' => $expireback->getFormatted('U', Time::TIMEZONE_GMT), 'message' => 'You tried to submit a wp-admin page.... this is not a WP site!', 'comment' => 'Bot or user submitted to wp-admin']);
         $block->save();
     } else {
         // Just record the hit.
         SystemLogModel::LogSecurityEvent('/wp-admin Honeypot GET', 'GET request to /wp-admin detected!');
     }
     $view->templatename = 'pages/wphoneypot/wpadmin.phtml';
     $view->mastertemplate = false;
 }
Example #25
0
/**
 * Handle an error and report it to the Core system log.
 *
 * @param      $errno
 * @param      $errstr
 * @param      $errfile
 * @param      $errline
 * @param null $errcontext
 */
function error_handler($errno, $errstr, $errfile, $errline, $errcontext = null){
	$type       = null;
	$fatal      = false;
	$code       = null;
	$class      = '';
	// The exception to this is when error_reporting is explictly set to 0.
	// This happens when a function is called with the "@" error suppressor.
	// In this event, I still want to log the error, but simply do not display it on the screen.
	// Damn f*****g "@" operator.....
	$suppressed = (error_reporting() === 0);

	switch($errno){
		case E_ERROR:
		case E_USER_ERROR:
			$fatal = true;
			$type  = 'error';
			$class = 'error';
			$code  = 'PHP Error';
			break;
		case E_WARNING:
		case E_USER_WARNING:
			$type = 'error';
			$class = 'warning';
			$code = 'PHP Warning';
			break;
		case E_NOTICE:
		case E_USER_NOTICE:
			$type = 'info';
			$class = 'info';
			$code = 'PHP Notice';
			break;
		case E_DEPRECATED:
		case E_USER_DEPRECATED:
			$type = 'info';
			$class = 'deprecated';
			$code = 'PHP Deprecated Notice';
			break;
		case E_STRICT:
			$type = 'info';
			$class = 'warning';
			$code = 'PHP Strict Warning';
			$suppressed = true;
			break;
		default:
			$type = 'info';
			$class = 'unknown';
			$code = 'Unknown PHP Error [' . $errno . ']';
			break;
	}

	if($suppressed){
		// Ignore suppressed errors when on production.
		// This is required because PHP < 7.0 has some functions that can only be called with the '@' operator.
		// Such as LDAP binding or many things in Smarty.
		if(!DEVELOPMENT_MODE){
			return;	
		}
		$code .= ' @SUPPRESSED';
	}

	// All errors/warnings/notices get logged!
	if($errfile && strpos($errfile, ROOT_PDIR) === 0){
		$details = '[src: ' . '/' . substr($errfile, strlen(ROOT_PDIR)) . ':' . $errline . '] ';
	}
	elseif($errfile){
		$details = '[src: ' . $errfile . ':' . $errline . '] ';
	}
	else{
		$details = '';
	}

	try{
		if(!\Core::GetComponent()){
			// SQUAK!  Core isn't even loaded yet!
			return;
		}

		// Allow external systems to hook into this event.
		\HookHandler::DispatchHook('/core/error_handler', $code, $errstr);

		$log = \SystemLogModel::Factory();
		$log->setFromArray([
			'type'    => $type,
			'code'    => $code,
			'message' => $details . $errstr
		]);
		$log->save();
	}
	catch(\Exception $e){
		// meh, try a traditional log.
		try{
			if(class_exists('Core\\Utilities\\Logger\\LogFile')){
				$log = new LogFile($type);
				$log->write($details . $errstr, $code);
			}
			else{
				error_log($details . $errstr);
			}
		}
		catch(\Exception $e){
			// Really meh now!
		}
	}

	// Display all errors when in development mode.
	if(DEVELOPMENT_MODE && !$suppressed){
		// The correct way to handle output is via EXEC_MODE.
		// HOWEVER, since the unit tests emulate a WEB mode so that the scripts behave as they would in the web browser,
		// this is not a reliable test here.
		if(isset($_SERVER['TERM']) || isset($_SERVER['SHELL'])){
			print_error_as_text($class, $code, $errstr);
		}
		elseif(EXEC_MODE == 'WEB'){
			print_error_as_html($class, $code, $errstr);
		}
		else{
			print_error_as_text($class, $code, $errstr);
		}
	}

	// If it's a fatal error and it's not in development mode, simply display a friendly error page instead.
	if($fatal){
		if(EXEC_MODE == 'WEB'){
			require(ROOT_PDIR . 'core/templates/halt_pages/fatal_error.inc.html');
		}
		exit();
	}
}