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