/** * @param string $sStr * @param bool $bLowerIfAscii = false * * @return string */ public static function IdnToAscii($sStr, $bLowerIfAscii = false) { $sStr = $bLowerIfAscii ? \MailSo\Base\Utils::StrToLowerIfAscii($sStr) : $sStr; $sUser = ''; $sDomain = $sStr; if (false !== \strpos($sStr, '@')) { $sUser = \MailSo\Base\Utils::GetAccountNameFromEmail($sStr); $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sStr); } if (0 < \strlen($sDomain) && \preg_match('/[^\\x20-\\x7E]/', $sDomain)) { try { $sDomain = self::idn()->encode($sDomain); } catch (\Exception $oException) { } } return ('' === $sUser ? '' : $sUser . '@') . $sDomain; }
/** * @param string $sEmail * @param string $sPassword * @param string $sSignMeToken = '' * @param string $sAdditionalCode = '' * @param string $bAdditionalCodeSignMe = false * * @return \RainLoop\Model\Account * @throws \RainLoop\Exceptions\ClientException */ public function LoginProcess(&$sEmail, &$sPassword, $sSignMeToken = '', $sAdditionalCode = '', $bAdditionalCodeSignMe = false) { $this->Plugins()->RunHook('filter.login-credentials.step-1', array(&$sEmail, &$sPassword)); $sEmail = \MailSo\Base\Utils::StrToLowerIfAscii(\MailSo\Base\Utils::Trim($sEmail)); if (false === \strpos($sEmail, '@')) { $this->Logger()->Write('The email address "' . $sEmail . '" is not complete', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); if (false === \strpos($sEmail, '@') && !!$this->Config()->Get('login', 'determine_user_domain', false)) { $sUserHost = \trim($this->Http()->GetHost(false, true, true)); $this->Logger()->Write('Determined user domain: ' . $sUserHost, \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); $bAdded = false; $iLimit = 14; $aDomainParts = \explode('.', $sUserHost); $oDomainProvider = $this->DomainProvider(); while (0 < \count($aDomainParts) && 0 < $iLimit) { $sLine = \trim(\implode('.', $aDomainParts), '. '); $oDomain = $oDomainProvider->Load($sLine, false); if ($oDomain && $oDomain instanceof \RainLoop\Model\Domain) { $bAdded = true; $this->Logger()->Write('Check "' . $sLine . '": OK (' . $sEmail . ' > ' . $sEmail . '@' . $sLine . ')', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); $sEmail = $sEmail . '@' . $sLine; break; } else { $this->Logger()->Write('Check "' . $sLine . '": NO', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); } \array_shift($aDomainParts); $iLimit--; } if (!$bAdded) { $sLine = $sUserHost; $oDomain = $oDomainProvider->Load($sLine, true); if ($oDomain && $oDomain instanceof \RainLoop\Model\Domain) { $bAdded = true; $this->Logger()->Write('Check "' . $sLine . '" with wildcard: OK (' . $sEmail . ' > ' . $sEmail . '@' . $sLine . ')', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); $sEmail = $sEmail . '@' . $sLine; } else { $this->Logger()->Write('Check "' . $sLine . '" with wildcard: NO', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); } } if (!$bAdded) { $this->Logger()->Write('Domain was not found!', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); } } $sDefDomain = \trim($this->Config()->Get('login', 'default_domain', '')); if (false === \strpos($sEmail, '@') && 0 < \strlen($sDefDomain)) { $this->Logger()->Write('Default domain "' . $sDefDomain . '" was used. (' . $sEmail . ' > ' . $sEmail . '@' . $sDefDomain . ')', \MailSo\Log\Enumerations\Type::INFO, 'LOGIN'); $sEmail = $sEmail . '@' . $sDefDomain; } } $this->Plugins()->RunHook('filter.login-credentials.step-2', array(&$sEmail, &$sPassword)); if (false === \strpos($sEmail, '@') || 0 === \strlen($sPassword)) { $this->loginErrorDelay(); throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::InvalidInputArgument); } $this->Logger()->AddSecret($sPassword); $sLogin = $sEmail; $this->Plugins()->RunHook('filter.login-credentials', array(&$sEmail, &$sLogin, &$sPassword)); $this->Logger()->AddSecret($sPassword); $this->Plugins()->RunHook('event.login-pre-login-provide', array()); $oAccount = null; try { $oAccount = $this->LoginProvide($sEmail, $sLogin, $sPassword, $sSignMeToken, true); if (!$oAccount instanceof \RainLoop\Model\Account) { throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError); } $this->Plugins()->RunHook('event.login-post-login-provide', array(&$oAccount)); if (!$oAccount instanceof \RainLoop\Model\Account) { throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AuthError); } } catch (\Exception $oException) { $this->loginErrorDelay(); $this->LoggerAuthHelper($oAccount); throw $oException; } // Two factor auth if ($this->TwoFactorAuthProvider()->IsActive()) { $aData = $this->getTwoFactorInfo($oAccount); if ($aData && isset($aData['IsSet'], $aData['Enable']) && !empty($aData['Secret']) && $aData['IsSet'] && $aData['Enable']) { $sSecretHash = \md5(APP_SALT . $aData['Secret'] . \RainLoop\Utils::Fingerprint()); $sSecretCookieHash = \RainLoop\Utils::GetCookie(self::AUTH_TFA_SIGN_ME_TOKEN_KEY, ''); if (empty($sSecretCookieHash) || $sSecretHash !== $sSecretCookieHash) { $sAdditionalCode = \trim($sAdditionalCode); if (empty($sAdditionalCode)) { $this->Logger()->Write('TFA: Required Code for ' . $oAccount->ParentEmailHelper() . ' account.'); $this->LoggerAuthHelper($oAccount); throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthRequired); } else { $this->Logger()->Write('TFA: Verify Code for ' . $oAccount->ParentEmailHelper() . ' account.'); $bGood = false; if (6 < \strlen($sAdditionalCode) && !empty($aData['BackupCodes'])) { $aBackupCodes = \explode(' ', \trim(\preg_replace('/[^\\d]+/', ' ', $aData['BackupCodes']))); $bGood = \in_array($sAdditionalCode, $aBackupCodes); if ($bGood) { $this->removeBackupCodeFromTwoFactorInfo($oAccount->ParentEmailHelper(), $sAdditionalCode); } } if ($bAdditionalCodeSignMe) { \RainLoop\Utils::SetCookie(self::AUTH_TFA_SIGN_ME_TOKEN_KEY, $sSecretHash, \time() + 60 * 60 * 24 * 14); } if (!$bGood && !$this->TwoFactorAuthProvider()->VerifyCode($aData['Secret'], $sAdditionalCode)) { $this->loginErrorDelay(); $this->LoggerAuthHelper($oAccount); throw new \RainLoop\Exceptions\ClientException(\RainLoop\Notifications::AccountTwoFactorAuthError); } } } } } try { $this->CheckMailConnection($oAccount, true); } catch (\Exception $oException) { $this->loginErrorDelay(); throw $oException; } return $oAccount; }
/** * @return \RainLoop\Service */ private function localHandle() { if (!\class_exists('MailSo\\Version')) { return $this; } $this->oActions->BootStart(); $sResult = ''; $bCached = false; $sQuery = $this->oActions->ParseQueryAuthString(); $this->oActions->Plugins()->RunHook('filter.http-query', array(&$sQuery)); $aPaths = \explode('/', $sQuery); $this->oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths)); $bAdmin = false; $sAdminPanelHost = $this->oActions->Config()->Get('security', 'admin_panel_host', ''); if (empty($sAdminPanelHost)) { $bAdmin = !empty($aPaths[0]) && \in_array(\strtolower($aPaths[0]), array('admin', 'cp')); } else { if (empty($aPaths[0]) && \MailSo\Base\Utils::StrToLowerIfAscii($sAdminPanelHost) === \MailSo\Base\Utils::StrToLowerIfAscii($this->oHttp->GetHost())) { $bAdmin = true; } } if ($this->oHttp->IsPost()) { $this->oHttp->ServerNoCache(); } if ($bAdmin && !$this->oActions->Config()->Get('security', 'allow_admin_panel', true)) { echo $this->oServiceActions->ErrorTemplates('Access Denied.', 'Access to the RainLoop Webmail Admin Panel is not allowed!', true); return $this; } $bIndex = true; if (0 < \count($aPaths) && !empty($aPaths[0]) && !$bAdmin && 'index' !== $aPaths[0]) { $bIndex = false; $sMethodName = 'Service' . $aPaths[0]; if (\method_exists($this->oServiceActions, $sMethodName) && \is_callable(array($this->oServiceActions, $sMethodName))) { $this->oServiceActions->SetQuery($sQuery)->SetPaths($aPaths); $sResult = \call_user_func(array($this->oServiceActions, $sMethodName)); } else { if (!$this->oActions->Plugins()->RunAdditionalPart($aPaths[0], $aPaths)) { $bIndex = true; } } } if ($bIndex) { @\header('Content-Security-Policy:'); @\header_remove('Content-Security-Policy'); @header('Content-Type: text/html; charset=utf-8'); $this->oHttp->ServerNoCache(); if (!@\is_dir(APP_DATA_FOLDER_PATH) || !@\is_writable(APP_DATA_FOLDER_PATH)) { echo $this->oServiceActions->ErrorTemplates('Permission denied!', 'RainLoop Webmail cannot access to the data folder "' . APP_DATA_FOLDER_PATH . '"'); return $this; } $aTemplateParameters = $this->indexTemplateParameters($bAdmin); $sCacheFileName = ''; if ($this->oActions->Config()->Get('labs', 'cache_system_data', true)) { $sCacheFileName = 'TMPL:' . $aTemplateParameters['{{BaseHash}}']; $sResult = $this->oActions->Cacher()->Get($sCacheFileName); } if (0 === \strlen($sResult)) { // $aTemplateParameters['{{BaseTemplates}}'] = $this->oServiceActions->compileTemplates($bAdmin, false); $sResult = \strtr(\file_get_contents(APP_VERSION_ROOT_PATH . 'app/templates/Index.html'), $aTemplateParameters); $sResult = \RainLoop\Utils::ClearHtmlOutput($sResult); if (0 < \strlen($sCacheFileName)) { $this->oActions->Cacher()->Set($sCacheFileName, $sResult); } } else { $bCached = true; } $sResult .= '<!--'; $sResult .= ' [time:' . \substr(\microtime(true) - APP_START, 0, 6); // $sResult .= '][version:'.APP_VERSION; if ($this->oActions->IsOpen()) { $sResult .= '][AGPLv3'; } $sResult .= '][cached:' . ($bCached ? 'true' : 'false'); // $sResult .= '][hash:'.$aTemplateParameters['{{BaseHash}}']; // $sResult .= '][session:'.\md5(\RainLoop\Utils::GetShortToken()); if (\RainLoop\Utils::IsOwnCloud()) { $sResult .= '][owncloud:true'; } $sResult .= '] //-->'; } // Output result echo $sResult; unset($sResult); $this->oActions->BootEnd(); return $this; }
public function UpdateDependentValues() { $this->Value = \trim($this->Value); $this->ValueCustom = \trim($this->ValueCustom); $this->TypeStr = \trim($this->TypeStr); if (0 < \strlen($this->Value)) { // lower if ($this->IsEmail()) { $this->Value = \MailSo\Base\Utils::StrToLowerIfAscii($this->Value); } // phones clear value for searching if ($this->IsPhone()) { $sPhone = $this->Value; $sPhone = \preg_replace('/^[+]+/', '', $sPhone); $sPhone = \preg_replace('/[^\\d]/', '', $sPhone); $this->ValueCustom = $sPhone; } } }
/** * @param string $sEmail * @param array $aEmails * @param bool $bCreateAuto = true * * @return bool */ public function IncFrec($sEmail, $aEmails, $bCreateAuto = true) { $self = $this; $aEmailsObjects = \array_map(function ($mItem) { $oResult = null; try { $oResult = \MailSo\Mime\Email::Parse(\trim($mItem)); } catch (\Exception $oException) { } return $oResult; }, $aEmails); $aEmailsObjects = \array_filter($aEmailsObjects, function ($oItem) { return !!$oItem; }); if (0 === \count($aEmailsObjects)) { throw new \InvalidArgumentException('Empty Emails argument'); } $this->SyncDatabase(); $iUserID = $this->getUserId($sEmail); $aExists = array(); $aEmailsToCreate = array(); $aEmailsToUpdate = array(); if ($bCreateAuto) { $sSql = 'SELECT prop_value FROM rainloop_ab_properties WHERE id_user = :id_user AND prop_type = :prop_type'; $oStmt = $this->prepareAndExecute($sSql, array(':id_user' => array($iUserID, \PDO::PARAM_INT), ':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT))); if ($oStmt) { $aFetch = $oStmt->fetchAll(\PDO::FETCH_ASSOC); if (\is_array($aFetch) && 0 < \count($aFetch)) { foreach ($aFetch as $aItem) { if ($aItem && !empty($aItem['prop_value'])) { $aExists[] = \MailSo\Base\Utils::StrToLowerIfAscii(\trim($aItem['prop_value'])); } } } } $aEmailsToCreate = \array_filter($aEmailsObjects, function ($oItem) use($aExists, &$aEmailsToUpdate) { if ($oItem) { $sEmail = \trim($oItem->GetEmail(true)); if (0 < \strlen($sEmail)) { $aEmailsToUpdate[] = $sEmail; return !\in_array($sEmail, $aExists); } } return false; }); } else { foreach ($aEmailsObjects as $oItem) { if ($oItem) { $sEmailUpdate = \trim($oItem->GetEmail(true)); if (0 < \strlen($sEmailUpdate)) { $aEmailsToUpdate[] = $sEmailUpdate; } } } } unset($aEmails, $aEmailsObjects); if (0 < \count($aEmailsToCreate)) { $oContact = new \RainLoop\Providers\AddressBook\Classes\Contact(); foreach ($aEmailsToCreate as $oEmail) { if ('' !== \trim($oEmail->GetEmail())) { $oPropEmail = new \RainLoop\Providers\AddressBook\Classes\Property(); $oPropEmail->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::EMAIl; $oPropEmail->Value = \trim($oEmail->GetEmail(true)); $oContact->Properties[] = $oPropEmail; } if ('' !== \trim($oEmail->GetDisplayName())) { $sFirst = $sLast = ''; $sFullName = $oEmail->GetDisplayName(); if (false !== \strpos($sFullName, ' ')) { $aNames = \explode(' ', $sFullName, 2); $sFirst = isset($aNames[0]) ? $aNames[0] : ''; $sLast = isset($aNames[1]) ? $aNames[1] : ''; } else { $sFirst = $sFullName; } if (0 < \strlen($sFirst)) { $oPropName = new \RainLoop\Providers\AddressBook\Classes\Property(); $oPropName->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::FIRST_NAME; $oPropName->Value = \trim($sFirst); $oContact->Properties[] = $oPropName; } if (0 < \strlen($sLast)) { $oPropName = new \RainLoop\Providers\AddressBook\Classes\Property(); $oPropName->Type = \RainLoop\Providers\AddressBook\Enumerations\PropertyType::LAST_NAME; $oPropName->Value = \trim($sLast); $oContact->Properties[] = $oPropName; } } if (0 < \count($oContact->Properties)) { $this->ContactSave($sEmail, $oContact); } $oContact->Clear(); } } $sSql = 'UPDATE rainloop_ab_properties SET prop_frec = prop_frec + 1 WHERE id_user = :id_user AND prop_type = :prop_type'; $aEmailsQuoted = \array_map(function ($mItem) use($self) { return $self->quoteValue($mItem); }, $aEmailsToUpdate); if (1 === \count($aEmailsQuoted)) { $sSql .= ' AND prop_value = ' . $aEmailsQuoted[0]; } else { $sSql .= ' AND prop_value IN (' . \implode(',', $aEmailsQuoted) . ')'; } return !!$this->prepareAndExecute($sSql, array(':id_user' => array($iUserID, \PDO::PARAM_INT), ':prop_type' => array(PropertyType::EMAIl, \PDO::PARAM_INT))); }
/** * @return \RainLoop\Service */ public function Handle() { if (!\class_exists('MailSo\\Version')) { return $this; } $this->oActions->BootStart(); $this->oActions->ParseQueryAuthString(); $bCached = false; $sResult = ''; $sQuery = \trim(\trim($this->oHttp->GetServer('QUERY_STRING', '')), ' /'); $iPos = \strpos($sQuery, '&'); if (0 < $iPos) { $sQuery = \substr($sQuery, 0, $iPos); } $this->oActions->Plugins()->RunHook('filter.http-query', array(&$sQuery)); $aPaths = \explode('/', $sQuery); $this->oActions->Plugins()->RunHook('filter.http-paths', array(&$aPaths)); $bAdmin = false; $sAdminPanelHost = $this->oActions->Config()->Get('security', 'admin_panel_host', ''); if (empty($sAdminPanelHost)) { $bAdmin = !empty($aPaths[0]) && \in_array(\strtolower($aPaths[0]), array('admin', 'cp')); } else { if (empty($aPaths[0]) && \MailSo\Base\Utils::StrToLowerIfAscii($sAdminPanelHost) === \MailSo\Base\Utils::StrToLowerIfAscii($this->oHttp->GetHost())) { $bAdmin = true; } } if ($bAdmin && !$this->oActions->Config()->Get('security', 'allow_admin_panel', true)) { echo $this->oActions->ErrorTemplates('Access Denied.', 'Access to the RainLoop Webmail Admin Panel is not allowed!', true); return $this; } $bIndex = true; if (0 < \count($aPaths) && !empty($aPaths[0]) && !$bAdmin && 'index' !== $aPaths[0]) { $bIndex = false; $sMethodName = 'Service' . $aPaths[0]; if (\method_exists($this->oServiceActions, $sMethodName) && \is_callable(array($this->oServiceActions, $sMethodName))) { $this->oServiceActions->SetQuery($sQuery)->SetPaths($aPaths); $sResult = \call_user_func(array($this->oServiceActions, $sMethodName)); } else { if (!$this->oActions->Plugins()->RunAdditionalPart($aPaths[0], $aPaths)) { $bIndex = true; } } } if ($bIndex) { @header('Content-Type: text/html; charset=utf-8'); $this->oHttp->ServerNoCache(); $aData = $this->startUpData($bAdmin); $sCacheFileName = ''; if ($this->oActions->Config()->Get('labs', 'cache_system_data', true)) { $sCacheFileName = 'TMPL:' . $aData['Hash']; $sResult = $this->oActions->Cacher()->Get($sCacheFileName); } if (0 === \strlen($sResult)) { $sJsBoot = \file_get_contents(APP_VERSION_ROOT_PATH . 'static/js/boot.js'); $sResult = \strtr(\file_get_contents(APP_VERSION_ROOT_PATH . 'app/templates/Index.html'), array('{{BaseRandHash}}' => \md5(\rand(1000, 9000) . \microtime(true)), '{{BaseAppDataScriptLink}}' => $bAdmin ? './?/AdminAppData/' : './?/AppData/', '{{BaseAppFaviconIcoFile}}' => $aData['FaviconIcoLink'], '{{BaseAppFaviconPngFile}}' => $aData['FaviconPngLink'], '{{BaseAppAppleTouchFile}}' => $aData['AppleTouchLink'], '{{BaseAppMainCssLink}}' => $aData['AppCssLink'], '{{BaseAppBootScriptSource}}' => $sJsBoot, '{{BaseAppLibsScriptLink}}' => $aData['LibJsLink'], '{{BaseAppEditorScriptLink}}' => $aData['EditorJsLink'], '{{BaseAppMainScriptLink}}' => $aData['AppJsLink'], '{{BaseAppLoadingDescription}}' => \htmlspecialchars($aData['LoadingDescription'], ENT_QUOTES | ENT_IGNORE, 'UTF-8'), '{{BaseDir}}' => \in_array($aData['Language'], array('ar', 'he', 'ur')) ? 'rtl' : 'ltr')); $sResult = \RainLoop\Utils::ClearHtmlOutput($sResult); if (0 < \strlen($sCacheFileName)) { $this->oActions->Cacher()->Set($sCacheFileName, $sResult); } } else { $bCached = true; } $sResult .= '<!--'; $sResult .= ' [version:' . APP_VERSION; $sResult .= '][time:' . \substr(\microtime(true) - APP_START, 0, 6); $sResult .= '][cached:' . ($bCached ? 'true' : 'false'); $sResult .= '][session:' . \md5(\RainLoop\Utils::GetShortToken()); $sResult .= '] -->'; } // Output result echo $sResult; unset($sResult); $this->oActions->BootEnd(); return $this; }
public function UpdateDependentValues() { $this->Value = \trim($this->Value); $this->ValueCustom = \trim($this->ValueCustom); $this->TypeStr = \trim($this->TypeStr); $this->ValueLower = ''; if (0 < \strlen($this->Value)) { // lower if ($this->IsEmail()) { $this->Value = \MailSo\Base\Utils::StrToLowerIfAscii($this->Value); } if ($this->IsName()) { $this->Value = \MailSo\Base\Utils::StripSpaces($this->Value); } // lower value for searching if (\MailSo\Base\Utils::FunctionExistsAndEnabled('mb_strtolower')) { $this->ValueLower = (string) @\mb_strtolower($this->Value, 'UTF-8'); } // phone value for searching if ($this->IsPhone()) { $sPhone = \trim($this->Value); $sPhone = \preg_replace('/^[+]+/', '', $sPhone); $sPhone = \preg_replace('/[^\\d]/', '', $sPhone); $this->ValueCustom = \trim($sPhone); } } }
/** * @return string */ public function ServiceMailto() { $sTo = \trim($this->oHttp->GetQuery('to', '')); if (!empty($sTo) && \preg_match('/^mailto:/i', $sTo)) { $oAccount = $this->oActions->GetAccountFromSignMeToken(); if ($oAccount) { $this->oActions->SetMailtoRequest(\MailSo\Base\Utils::StrToLowerIfAscii($sTo)); } } $this->oActions->Location('./'); return ''; }