private function _forgotPassword1(){
		$view = $this->getView();
		$request = $this->getPageRequest();


		// Create a simple form to render.  This is better than doing it in the template.
		$form = new Form();
		$form->set('method', 'POST');

		if(\Core\user()->exists()){
			// This may happen with the enable-password feature for facebook accounts.
			// They shouldn't have the option to change the email, but it should display where the information will go to.
			$form->addElement('system', ['name' => 'email', 'value' => \Core\user()->get('email')]);
			$current = \Core\user()->get('email');
		}
		else{
			$form->addElement('text', ['name' => 'email', 'title' => 'Email', 'required' => true]);
			$current = false;
		}

		$form->addElement('submit', ['name' => 'submit', 'value' => 'Send Reset Instructions']);

		$view->title = 'Forgot Password';
		// This is step 1
		$view->assign('step', 1);
		$view->assign('form', $form);
		$view->assign('current', $current);
		$view->assign('can_change_email', \ConfigHandler::Get('/user/email/allowchanging'));
		// Google has no business indexing user-action pages.
		$view->addMetaName('robots', 'noindex');

		// There's really nothing to do here except for check the email and send it.

		if($request->isPost()){

			if(\Core\user()->exists()){
				/** @var UserModel $u */
				$u = \Core\user();
			}
			else{
				/** @var UserModel $u */
				$u = UserModel::Find(array('email' => $_POST['email']), 1);
			}

			if(!$u){
				\Core\set_message('t:MESSAGE_ERROR_USER_LOGIN_EMAIL_NOT_FOUND');
				SystemLogModel::LogSecurityEvent('/user/forgotpassword/send', 'Failed Forgot Password. Invalid email requested for reset: [' . $_POST['email'] . ']');
				return;
			}

			// Use the Nonce system to generate a one-time key with this user's data.
			$nonce = NonceModel::Generate(
				'20 minutes',
				null,
				[
					'type' => 'password-reset',
					'user' => $u->get('id'),
				]
			);

			$link = '/datastoreauth/forgotpassword/' . $nonce;

			$e = new Email();
			$e->setSubject('Forgot Password Request');
			$e->to($u->get('email'));
			$e->templatename = 'emails/user/datastoreauth_forgotpassword.tpl';
			$e->assign('link', \Core\resolve_link($link));
			$e->assign('ip', REMOTE_IP);
			try{
				$e->send();
				SystemLogModel::LogSecurityEvent('/user/forgotpassword/send', 'Forgot password request sent successfully', null, $u->get('id'));
			}
			catch(Exception $e){
				\Core\set_message('Error sending the email, ' . $e->getMessage(), 'error');
				SystemLogModel::LogErrorEvent('/user/forgotpassword/send', $e->getMessage());
				return;
			}

			// Otherwise, it must have sent, (hopefully)...
			\Core\set_message('t:MESSAGE_SUCCESS_PLEASE_CHECK_EMAIL_FOR_PASSWORD_RESET_INSTRUCTIONS');
			\core\redirect('/');
		}
	}
Ejemplo n.º 2
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();
	}
Ejemplo n.º 3
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();	
		}
	}
 /**
  * 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]);
 }