/** * @return string */ public function OutLogin() { $sLogin = $this->sLogin; if ($this->oDomain->OutShortLogin()) { $sLogin = \MailSo\Base\Utils::GetAccountNameFromEmail($this->sLogin); } return $sLogin; }
/** * @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 \RainLoop\Model\Account $oAccount * @param string $sPrevPassword * @param string $sNewPassword * * @return bool */ public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword) { $this->WriteLog('Virtualmin: Try to change password for ' . $oAccount->Email()); $bResult = false; if (!empty($this->sHost) && !empty($this->sAdminUser) && !empty($this->sAdminPassword) && $oAccount) { $this->WriteLog('Virtualmin:[Check] Required Fields Present'); $sEmail = \trim(\strtolower($oAccount->Email())); $sEmailUser = \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail); $sEmailDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail); $sHost = \rtrim(\trim($this->sHost), '/'); $sUrl = $sHost . '/virtual-server/remote.cgi'; $sAdminUser = $this->sAdminUser; $sAdminPassword = $this->sAdminPassword; $iCode = 0; $aPost = array('user' => $sEmailUser, 'pass' => $sNewPassword, 'domain' => $sEmailDomain, 'program' => 'modify-user'); $aOptions = array(CURLOPT_URL => $sUrl, CURLOPT_HEADER => false, CURLOPT_FAILONERROR => true, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => \http_build_query($aPost, '', '&'), CURLOPT_TIMEOUT => 20, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_USERPWD => $sAdminUser . ':' . $sAdminPassword); $oCurl = \curl_init(); \curl_setopt_array($oCurl, $aOptions); $this->WriteLog('Virtualmin: Send post request: ' . $sUrl); $mResult = \curl_exec($oCurl); $iCode = (int) \curl_getinfo($oCurl, CURLINFO_HTTP_CODE); $sContentType = (string) \curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE); $this->WriteLog('Virtualmin: Post request result: (Status: ' . $iCode . ', ContentType: ' . $sContentType . ')'); if (false === $mResult || 200 !== $iCode) { $this->WriteLog('Virtualmin: Error: ' . \curl_error($oCurl), \MailSo\Log\Enumerations\Type::WARNING); } if (\is_resource($oCurl)) { \curl_close($oCurl); } if (false !== $mResult && 200 === $iCode) { $aRes = null; @\parse_str($mResult, $aRes); if (\is_array($aRes) && (!isset($aRes['error']) || (int) $aRes['error'] !== 1)) { $iPos = \strpos($mResult, 'Exit status: '); if ($iPos !== false) { $aStatus = \explode(' ', $mResult); $sStatus = \trim(\array_pop($aStatus)); if ('0' === $sStatus) { $this->WriteLog('Virtualmin: Password Change Status: Success'); $bResult = true; } else { $this->WriteLog('Virtualmin[Error]: Response: ' . $mResult); } } } else { $this->WriteLog('Virtualmin[Error]: Response: ' . $mResult); } } else { $this->WriteLog('Virtualmin[Error]: Empty Response: Code: ' . $iCode); } } return $bResult; }
/** * @param \RainLoop\Model\Account $oAccount * @param string $sPrevPassword * @param string $sNewPassword * * @return bool */ public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword) { $bResult = false; try { $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email()); $sUserDn = \strtr($this->sUserDnFormat, array('{domain}' => $sDomain, '{domain:dc}' => 'dc=' . \strtr($sDomain, array('.' => ',dc=')), '{email}' => $oAccount->Email(), '{email:user}' => \MailSo\Base\Utils::GetAccountNameFromEmail($oAccount->Email()), '{email:domain}' => $sDomain, '{login}' => $oAccount->Login(), '{imap:login}' => $oAccount->Login(), '{imap:host}' => $oAccount->DomainIncHost(), '{imap:port}' => $oAccount->DomainIncPort())); $oCon = @\ldap_connect($this->sHostName); if ($oCon) { @\ldap_set_option($oCon, LDAP_OPT_PROTOCOL_VERSION, 3); if (!@\ldap_bind($oCon, $sUserDn, $sPrevPassword)) { if ($this->oLogger) { $sError = $oCon ? @\ldap_error($oCon) : ''; $iErrno = $oCon ? @\ldap_errno($oCon) : 0; $this->oLogger->Write('ldap_bind error: ' . $sError . ' (' . $iErrno . ')', \MailSo\Log\Enumerations\Type::WARNING, 'LDAP'); } return false; } } $sEncodedNewPassword = $sNewPassword; switch (\strtolower($this->sPasswordEncType)) { case 'sha': switch (true) { default: case \function_exists('sha1'): $sEncodedNewPassword = '******' . \base64_encode(\pack('H*', \sha1($sNewPassword))); break; case \function_exists('hash'): $sEncodedNewPassword = '******' . \base64_encode(\hash('sha1', $sNewPassword, true)); break; case \function_exists('mhash') && defined('MHASH_SHA1'): $sEncodedNewPassword = '******' . \base64_encode(\mhash(MHASH_SHA1, $sNewPassword)); break; } break; case 'md5': $sEncodedNewPassword = '******' . \base64_encode(\pack('H*', \md5($sNewPassword))); break; case 'crypt': $sEncodedNewPassword = '******' . \crypt($sNewPassword, $this->getSalt(2)); break; } $aEntry = array(); $aEntry[$this->sPasswordField] = (string) $sEncodedNewPassword; if (!!@\ldap_modify($oCon, $sUserDn, $aEntry)) { $bResult = true; } else { if ($this->oLogger) { $sError = $oCon ? @\ldap_error($oCon) : ''; $iErrno = $oCon ? @\ldap_errno($oCon) : 0; $this->oLogger->Write('ldap_modify error: ' . $sError . ' (' . $iErrno . ')', \MailSo\Log\Enumerations\Type::WARNING, 'LDAP'); } } } catch (\Exception $oException) { if ($this->oLogger) { $this->oLogger->WriteException($oException, \MailSo\Log\Enumerations\Type::WARNING, 'LDAP'); } $bResult = false; } return $bResult; }
/** * @return string */ public function GetAccountName() { return \MailSo\Base\Utils::GetAccountNameFromEmail($this->GetEmail(false)); }
/** * @param string $sLine * @param \RainLoop\Model\Account $oAccount = null * @param bool $bUrlEncode = false * * @return string */ private function compileLogParams($sLine, $oAccount = null, $bUrlEncode = false) { if (false !== \strpos($sLine, '{date:')) { $iTimeOffset = (int) $this->Config()->Get('logs', 'time_offset', 0); $sLine = \preg_replace_callback('/\\{date:([^}]+)\\}/', function ($aMatch) use($iTimeOffset, $bUrlEncode) { return \RainLoop\Utils::UrlEncode(\MailSo\Log\Logger::DateHelper($aMatch[1], $iTimeOffset), $bUrlEncode); }, $sLine); $sLine = \preg_replace('/\\{date:([^}]*)\\}/', 'date', $sLine); } if (false !== \strpos($sLine, '{imap:') || false !== \strpos($sLine, '{smtp:')) { if (!$oAccount) { $this->ParseQueryAuthString(); $oAccount = $this->getAccountFromToken(false); } if ($oAccount) { $sLine = \str_replace('{imap:login}', \RainLoop\Utils::UrlEncode($oAccount->IncLogin(), $bUrlEncode), $sLine); $sLine = \str_replace('{imap:host}', \RainLoop\Utils::UrlEncode($oAccount->DomainIncHost(), $bUrlEncode), $sLine); $sLine = \str_replace('{imap:port}', \RainLoop\Utils::UrlEncode($oAccount->DomainIncPort(), $bUrlEncode), $sLine); $sLine = \str_replace('{smtp:login}', \RainLoop\Utils::UrlEncode($oAccount->OutLogin(), $bUrlEncode), $sLine); $sLine = \str_replace('{smtp:host}', \RainLoop\Utils::UrlEncode($oAccount->DomainOutHost(), $bUrlEncode), $sLine); $sLine = \str_replace('{smtp:port}', \RainLoop\Utils::UrlEncode($oAccount->DomainOutPort(), $bUrlEncode), $sLine); } $sLine = \preg_replace('/\\{imap:([^}]*)\\}/i', 'imap', $sLine); $sLine = \preg_replace('/\\{smtp:([^}]*)\\}/i', 'smtp', $sLine); } if (false !== \strpos($sLine, '{request:')) { if (false !== \strpos($sLine, '{request:ip}')) { $sLine = \str_replace('{request:ip}', \RainLoop\Utils::UrlEncode($this->Http()->GetClientIp($this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $bUrlEncode), $sLine); } if (false !== \strpos($sLine, '{request:domain}')) { $sLine = \str_replace('{request:domain}', \RainLoop\Utils::UrlEncode($this->Http()->GetHost(false, true, true), $bUrlEncode), $sLine); } if (false !== \strpos($sLine, '{request:domain-clear}')) { $sLine = \str_replace('{request:domain-clear}', \RainLoop\Utils::UrlEncode(\MailSo\Base\Utils::GetClearDomainName($this->Http()->GetHost(false, true, true)), $bUrlEncode), $sLine); } $sLine = \preg_replace('/\\{request:([^}]*)\\}/i', 'request', $sLine); } if (false !== \strpos($sLine, '{user:'******'{user:uid}')) { $sLine = \str_replace('{user:uid}', \RainLoop\Utils::UrlEncode(\base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), $bUrlEncode), $sLine); } if (false !== \strpos($sLine, '{user:ip}')) { $sLine = \str_replace('{user:ip}', \RainLoop\Utils::UrlEncode($this->Http()->GetClientIp($this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $bUrlEncode), $sLine); } if (\preg_match('/\\{user:(email|login|domain)\\}/i', $sLine)) { if (!$oAccount) { $this->ParseQueryAuthString(); $oAccount = $this->getAccountFromToken(false); } if ($oAccount) { $sEmail = $oAccount->Email(); $sLine = \str_replace('{user:email}', \RainLoop\Utils::UrlEncode($sEmail, $bUrlEncode), $sLine); $sLine = \str_replace('{user:login}', \RainLoop\Utils::UrlEncode(\MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $bUrlEncode), $sLine); $sLine = \str_replace('{user:domain}', \RainLoop\Utils::UrlEncode(\MailSo\Base\Utils::GetDomainFromEmail($sEmail), $bUrlEncode), $sLine); $sLine = \str_replace('{user:domain-clear}', \RainLoop\Utils::UrlEncode(\MailSo\Base\Utils::GetClearDomainName(\MailSo\Base\Utils::GetDomainFromEmail($sEmail)), $bUrlEncode), $sLine); } } $sLine = \preg_replace('/\\{user:([^}]*)\\}/i', 'unknown', $sLine); } if (false !== \strpos($sLine, '{labs:')) { $sLine = \preg_replace_callback('/\\{labs:rand:([1-9])\\}/', function ($aMatch) { return \rand(\pow(10, $aMatch[1] - 1), \pow(10, $aMatch[1]) - 1); }, $sLine); $sLine = \preg_replace('/\\{labs:([^}]*)\\}/', 'labs', $sLine); } return $sLine; }
/** * @param string $sEmail * @param string $sLogin = '' * * @return bool */ public function ValidateWhiteList($sEmail, $sLogin = '') { $sW = \trim($this->sWhiteList); if (0 < strlen($sW)) { $sEmail = \MailSo\Base\Utils::IdnToUtf8($sEmail, true); $sLogin = \MailSo\Base\Utils::IdnToUtf8($sLogin); $sW = \preg_replace('/([^\\s]+)@[^\\s]*/', '$1', $sW); $sW = ' ' . \trim(\preg_replace('/[\\s;,\\r\\n\\t]+/', ' ', $sW)) . ' '; $sUserPart = \MailSo\Base\Utils::GetAccountNameFromEmail(0 < \strlen($sLogin) ? $sLogin : $sEmail); return false !== \strpos($sW, ' ' . $sUserPart . ' '); } return true; }
/** * @param \RainLoop\Model\Account $oAccount * @param string $sPrevPassword * @param string $sNewPassword * * @return bool */ public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword) { if ($this->oLogger) { $this->oLogger->Write('Try to change password for ' . $oAccount->Email()); } if (!\class_exists('xmlapi')) { include_once __DIR__ . '/xmlapi.php'; } $bResult = false; if (!empty($this->sHost) && 0 < $this->iPost && 0 < \strlen($this->sUser) && 0 < \strlen($this->sPassword) && $oAccount && \class_exists('xmlapi')) { $sEmail = $oAccount->Email(); $sEmailUser = \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail); $sEmailDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail); $sHost = $this->sHost; $sHost = \str_replace('{user:domain}', $sEmailDomain, $sHost); $sUser = $this->sUser; $sUser = \str_replace('{user:email}', $sEmail, $sUser); $sUser = \str_replace('{user:login}', $sEmailUser, $sUser); $sPassword = $this->sPassword; $sPassword = \str_replace('{user:password}', $oAccount->Password(), $sPassword); try { $oXmlApi = new \xmlapi($sHost); $oXmlApi->set_port($this->iPost); $oXmlApi->set_protocol($this->bSsl ? 'https' : 'http'); $oXmlApi->set_debug(false); $oXmlApi->set_output('json'); // $oXmlApi->set_http_client('fopen'); $oXmlApi->set_http_client('curl'); $oXmlApi->password_auth($sUser, $sPassword); $aArgs = array('email' => $sEmailUser, 'domain' => $sEmailDomain, 'password' => $sNewPassword); $sResult = $oXmlApi->api2_query($sUser, 'Email', 'passwdpop', $aArgs); if ($sResult) { if ($this->oLogger) { $this->oLogger->Write('CPANEL: ' . $sResult, \MailSo\Log\Enumerations\Type::INFO); } $aResult = @\json_decode($sResult, true); $bResult = isset($aResult['cpanelresult']['data'][0]['result']) && !!$aResult['cpanelresult']['data'][0]['result']; } if (!$bResult && $this->oLogger) { $this->oLogger->Write('CPANEL: ' . $sResult, \MailSo\Log\Enumerations\Type::ERROR); } } catch (\Exception $oException) { if ($this->oLogger) { $this->oLogger->WriteException($oException); } } } else { if ($this->oLogger) { $this->oLogger->Write('CPANEL: Incorrent configuration data', \MailSo\Log\Enumerations\Type::ERROR); } } return $bResult; }
/** * @param \RainLoop\Account $oAccount * @param string $sPrevPassword * @param string $sNewPassword * * @return bool */ public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword) { if ($this->oLogger) { $this->oLogger->Write('Try to change password for ' . $oAccount->Email()); } if (empty($this->mHost) || empty($this->mDatabase) || empty($this->mColumn) || empty($this->mTable)) { return false; } $bResult = false; $sDsn = 'mysql:host=' . $this->mHost . ';dbname=' . $this->mDatabase . ';charset=utf8'; $aOptions = array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_PERSISTENT => true, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); $sLoginPart = \MailSo\Base\Utils::GetAccountNameFromEmail($oAccount->Email()); $sDomainPart = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email()); try { $oConn = new PDO($sDsn, $this->mUser, $this->mPass, $aOptions); $oSelect = $oConn->prepare('SELECT ' . $this->mColumn . ' FROM ' . $this->mTable . ' WHERE pw_name=? AND pw_domain=? LIMIT 1'); $oSelect->execute(array($sLoginPart, $sDomainPart)); $aColCrypt = $oSelect->fetchAll(PDO::FETCH_ASSOC); $sCryptPass = isset($aColCrypt[0][$this->mColumn]) ? $aColCrypt[0][$this->mColumn] : ''; if (0 < \strlen($sCryptPass) && \crypt($sPrevPassword, $sCryptPass) === $sCryptPass) { $oUpdate = $oConn->prepare('UPDATE ' . $this->mTable . ' SET ' . $this->mColumn . '=ENCRYPT(?,concat("$1$",right(md5(rand()), 8 ),"$")), pw_clear_passwd=\'\' WHERE pw_name=? AND pw_domain=?'); $oUpdate->execute(array($sNewPassword, $sLoginPart, $sDomainPart)); $bResult = true; if ($this->oLogger) { $this->oLogger->Write('Success! Password changed.'); } } else { $bResult = false; if ($this->oLogger) { $this->oLogger->Write('Something went wrong. Either current password is incorrect, or new password does not match criteria.'); } } } catch (\Exception $oException) { $bResult = false; if ($this->oLogger) { $this->oLogger->WriteException($oException); } } return $bResult; }
/** * @param \RainLoop\Model\Account $oAccount * @param string $sQuery * * @return array */ private function ldapSearch($oAccount, $sQuery) { $sSearchEscaped = $this->escape($sQuery); $aResult = array(); $oCon = @\ldap_connect($this->sHostName, $this->iHostPort); if ($oCon) { $this->oLogger->Write('ldap_connect: connected', \MailSo\Log\Enumerations\Type::INFO, 'LDAP'); @\ldap_set_option($oCon, LDAP_OPT_PROTOCOL_VERSION, 3); if (!@\ldap_bind($oCon, $this->sAccessDn, $this->sAccessPassword)) { $this->logLdapError($oCon, 'ldap_bind'); return $aResult; } $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email()); $sSearchDn = \strtr($this->sUsersDn, array('{domain}' => $sDomain, '{domain:dc}' => 'dc=' . \strtr($sDomain, array('.' => ',dc=')), '{email}' => $oAccount->Email(), '{email:user}' => \MailSo\Base\Utils::GetAccountNameFromEmail($oAccount->Email()), '{email:domain}' => $sDomain, '{login}' => $oAccount->Login(), '{imap:login}' => $oAccount->Login(), '{imap:host}' => $oAccount->DomainIncHost(), '{imap:port}' => $oAccount->DomainIncPort())); $aEmails = empty($this->sEmailField) ? array() : \explode(',', $this->sEmailField); $aNames = empty($this->sNameField) ? array() : \explode(',', $this->sNameField); $aEmails = \array_map('trim', $aEmails); $aNames = \array_map('trim', $aNames); $aFields = \array_merge($aEmails, $aNames); $aItems = array(); $sSubFilter = ''; foreach ($aFields as $sItem) { if (!empty($sItem)) { $aItems[] = $sItem; $sSubFilter .= '(' . $sItem . '=*' . $sSearchEscaped . '*)'; } } $sFilter = '(&(objectclass=' . $this->sObjectClass . ')'; $sFilter .= (1 < count($aItems) ? '(|' : '') . $sSubFilter . (1 < count($aItems) ? ')' : ''); $sFilter .= ')'; $this->oLogger->Write('ldap_search: start: ' . $sSearchDn . ' / ' . $sFilter, \MailSo\Log\Enumerations\Type::INFO, 'LDAP'); $oS = @\ldap_search($oCon, $sSearchDn, $sFilter, $aItems, 0, 30, 30); if ($oS) { $aEntries = @\ldap_get_entries($oCon, $oS); if (is_array($aEntries)) { if (isset($aEntries['count'])) { unset($aEntries['count']); } foreach ($aEntries as $aItem) { if ($aItem) { $sName = $sEmail = ''; list($sEmail, $sName) = $this->findNameAndEmail($aItem, $aEmails, $aNames); if (!empty($sEmail)) { $aResult[] = array($sEmail, $sName); } } } } else { $this->logLdapError($oCon, 'ldap_get_entries'); } } else { $this->logLdapError($oCon, 'ldap_search'); } } else { return $aResult; } return $aResult; }
/** * @return string */ private function compileLogFileName() { $sFileName = (string) $this->Config()->Get('logs', 'filename', ''); if (false !== \strpos($sFileName, '{date:')) { $sFileName = \preg_replace_callback('/\\{date:([^}]+)\\}/', function ($aMatch) { return \gmdate($aMatch[1]); }, $sFileName); $sFileName = \preg_replace('/\\{date:([^}]*)\\}/', 'date', $sFileName); } if (false !== \strpos($sFileName, '{user:'******'{user:uid}')) { $sFileName = \str_replace('{user:uid}', \base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), $sFileName); } if (false !== \strpos($sFileName, '{user:ip}')) { $sFileName = \str_replace('{user:ip}', $this->Http()->GetClientIp(), $sFileName); } if (\preg_match('/\\{user:(email|login|domain)\\}/i', $sFileName)) { $this->ParseQueryAuthString(); $oAccount = $this->getAccountFromToken(false); if ($oAccount) { $sEmail = $oAccount->Email(); $sFileName = \str_replace('{user:email}', $sEmail, $sFileName); $sFileName = \str_replace('{user:login}', \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sFileName); $sFileName = \str_replace('{user:domain}', \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sFileName); } } $sFileName = \preg_replace('/\\{user:([^}]*)\\}/i', 'unknown', $sFileName); } if (false !== \strpos($sFileName, '{labs:')) { $sFileName = \preg_replace_callback('/\\{labs:rand:([1-9])\\}/', function ($aMatch) { return \rand(\pow(10, $aMatch[1] - 1), \pow(10, $aMatch[1]) - 1); }, $sFileName); $sFileName = \preg_replace('/\\{labs:([^}]*)\\}/', 'labs', $sFileName); } if (0 === \strlen($sFileName)) { $sFileName = 'rainloop-log.txt'; } $sFileName = \preg_replace('/[\\/]+/', '/', \preg_replace('/[.]+/', '.', $sFileName)); $sFileName = \preg_replace('/[^a-zA-Z0-9@_+=\\-\\.\\/!()\\[\\]]/', '', $sFileName); return $sFileName; }