1: <?php
2: /**
3: * This code is licensed under AGPLv3 license or Afterlogic Software License
4: * if commercial version of the product was purchased.
5: * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
6: */
7:
8: namespace Aurora\Modules\CpanelIntegrator;
9:
10: use Aurora\Modules\Core\Classes\Tenant;
11: use Aurora\Modules\MailDomains\Classes\Domain;
12: use Aurora\System\Exceptions\ApiException;
13:
14: /**
15: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
16: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
17: * @copyright Copyright (c) 2023, Afterlogic Corp.
18: *
19: * @package Modules
20: */
21: class Module extends \Aurora\System\Module\AbstractModule
22: {
23: public const UAPI = '3';
24: private $oCpanel = [];
25:
26: protected static $bAllowDeleteFromMailServerIfPossible = false;
27: protected $aManagers = [
28: 'Aliases' => null
29: ];
30:
31: public $oMailModule = null;
32:
33: public function init()
34: {
35: $this->aErrors = [
36: Enums\ErrorCodes::DomainOutsideTenant => $this->i18N('ERROR_DOMAIN_OUTSIDE_TENANT'),
37: Enums\ErrorCodes::AliaMatchesExistingEmail => $this->i18N('ERROR_CREATE_ALIAS'),
38: Enums\ErrorCodes::AliasAlreadyExists => $this->i18N('ERROR_ALIAS_ALREADY_EXISTS'),
39: ];
40: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
41: if ($this->getConfig('AllowCreateDeleteAccountOnCpanel', false) && $oAuthenticatedUser &&
42: ($oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) {
43: // Subscription shouldn't work for Anonymous because Signup subscription will work
44: // Subscription shouldn't work for Normal user because CPanel account should be created only for first user account
45: $this->subscribeEvent('Mail::CreateAccount::before', array($this, 'onBeforeCreateAccount'));
46: $this->subscribeEvent('Mail::BeforeDeleteAccount', array($this, 'onBeforeDeleteAccount'));
47: }
48: $this->subscribeEvent('MailSignup::Signup::before', [$this, 'onAfterSignup']);
49: $this->subscribeEvent('Mail::Account::ToResponseArray', array($this, 'onMailAccountToResponseArray'));
50: $this->subscribeEvent('Mail::ChangeAccountPassword', array($this, 'onChangeAccountPassword'));
51: $this->subscribeEvent('StandardResetPassword::ChangeAccountPassword', array($this, 'onChangeAccountPassword'));
52: $this->subscribeEvent('Mail::UpdateForward::before', array($this, 'onBeforeUpdateForward'));
53: $this->subscribeEvent('Mail::GetForward::before', array($this, 'onBeforeGetForward'));
54: $this->subscribeEvent('Mail::GetAutoresponder::before', array($this, 'onBeforeGetAutoresponder'));
55: $this->subscribeEvent('Mail::UpdateAutoresponder::before', array($this, 'onBeforeUpdateAutoresponder'));
56: $this->subscribeEvent('Mail::GetFilters::before', array($this, 'onBeforeGetFilters'));
57: $this->subscribeEvent('Mail::UpdateFilters::before', array($this, 'onBeforeUpdateFilters'));
58: $this->subscribeEvent('Mail::UpdateQuota', array($this, 'onUpdateQuota'));
59: $this->subscribeEvent('Mail::SaveMessage::before', array($this, 'onBeforeSendOrSaveMessage'));
60: $this->subscribeEvent('Mail::SendMessage::before', array($this, 'onBeforeSendOrSaveMessage'));
61:
62: $this->subscribeEvent('AdminPanelWebclient::DeleteEntities::before', array($this, 'onBeforeDeleteEntities'));/** @deprecated since version 8.3.7 **/
63: $this->subscribeEvent('Core::DeleteTenant::before', array($this, 'onBeforeDeleteEntities'));
64: $this->subscribeEvent('Core::DeleteTenants::before', array($this, 'onBeforeDeleteEntities'));
65: $this->subscribeEvent('Core::DeleteUser::before', array($this, 'onBeforeDeleteEntities'));
66: $this->subscribeEvent('Core::DeleteUsers::before', array($this, 'onBeforeDeleteEntities'));
67: $this->subscribeEvent('Mail::DeleteServer::before', array($this, 'onBeforeDeleteEntities'));
68: $this->subscribeEvent('MailDomains::DeleteDomains::before', array($this, 'onBeforeDeleteEntities'));
69: $this->subscribeEvent('Mail::IsEmailAllowedForCreation::after', array($this, 'onAfterIsEmailAllowedForCreation'));
70: }
71:
72: /**
73: * @param string $sManager
74: * @return object
75: */
76: public function getManager($sManager)
77: {
78: if ($this->aManagers[$sManager] === null) {
79: $sManagerClass = Module::getNamespace() . "\\Managers\\" . $sManager;
80: $this->aManagers[$sManager] = new $sManagerClass($this);
81: }
82:
83: return $this->aManagers[$sManager];
84: }
85:
86: /**
87: * Connects to cPanel. Uses main credentials if $iTenantId has not been passed, otherwise - tenant credentials to cPanel.
88: * @param int $iTenantId
89: * @return object
90: */
91: protected function getCpanel($iTenantId = 0)
92: {
93: if (!isset($this->oCpanel[$iTenantId])) {
94: $sHost = $this->getConfig('CpanelHost', '');
95: $sPort = $this->getConfig('CpanelPort', '');
96: $sUser = $this->getConfig('CpanelUser', '');
97: $sPassword = $this->getConfig('CpanelPassword', '');
98:
99: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
100: if ($iTenantId !== 0 && $oAuthenticatedUser && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
101: $oSettings = $this->GetModuleSettings();
102: $oTenant = \Aurora\System\Api::getTenantById($iTenantId);
103: $sHost = $oSettings->GetTenantValue($oTenant->Name, 'CpanelHost', '');
104: $sPort = $oSettings->GetTenantValue($oTenant->Name, 'CpanelPort', '');
105: $sUser = $oSettings->GetTenantValue($oTenant->Name, 'CpanelUser', '');
106: $sPassword = $oSettings->GetTenantValue($oTenant->Name, 'CpanelPassword', '');
107: }
108:
109: $this->oCpanel[$iTenantId] = new \Gufy\CpanelPhp\Cpanel([
110: 'host' => "https://" . $sHost . ":" . $sPort,
111: 'username' => $sUser,
112: 'auth_type' => 'password',
113: 'password' => $sPassword,
114: ]);
115:
116: $this->oCpanel[$iTenantId]->setTimeout(30);
117: }
118:
119: return $this->oCpanel[$iTenantId];
120: }
121:
122: /**
123: * Executes cPanel command, logs parameters and the result.
124: * @param object $oCpanel
125: * @param string $sModule
126: * @param string $sFunction
127: * @param array $aParams
128: * @return string
129: */
130: protected function executeCpanelAction($oCpanel, $sModule, $sFunction, $aParams)
131: {
132: // There are no params in log because they can contain private data (password for example)
133: \Aurora\System\Api::Log('cPanel execute action. Module ' . $sModule . '. Function ' . $sFunction);
134: $sResult = $oCpanel->execute_action(self::UAPI, $sModule, $sFunction, $oCpanel->getUsername(), $aParams);
135: \Aurora\System\Api::Log($sResult);
136: return $sResult;
137: }
138:
139: /**
140: * Creates account with credentials specified in registration form
141: *
142: * @param array $aArgs New account credentials.
143: * @param type $mResult Is passed by reference.
144: */
145: public function onAfterSignup($aArgs, &$mResult)
146: {
147: if (isset($aArgs['Login']) && isset($aArgs['Password']) && !empty(trim($aArgs['Password'])) && !empty(trim($aArgs['Login']))) {
148: $sLogin = trim($aArgs['Login']);
149: $sPassword = trim($aArgs['Password']);
150: $sFriendlyName = isset($aArgs['Name']) ? trim($aArgs['Name']) : '';
151: $bSignMe = isset($aArgs['SignMe']) ? (bool) $aArgs['SignMe'] : false;
152: $bPrevState = \Aurora\System\Api::skipCheckUserRole(true);
153: $iUserId = \Aurora\Modules\Core\Module::Decorator()->CreateUser(0, $sLogin);
154: $oUser = \Aurora\System\Api::getUserById((int) $iUserId);
155: $oCpanel = null;
156: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
157: try {
158: $oCpanel = $this->getCpanel($oUser->IdTenant);
159: } catch(\Exception $oException) {
160: }
161:
162: if ($oCpanel) {
163: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oUser->PublicId);
164: if (!empty($sDomain) && $this->isDomainSupported($sDomain)) {
165: $iQuota = (int) $this->getConfig('UserDefaultQuotaMB', 1);
166: try {
167: $sCpanelResponse = $this->executeCpanelAction(
168: $oCpanel,
169: 'Email',
170: 'add_pop',
171: [
172: 'email' => $sLogin,
173: 'password' => $sPassword,
174: 'quota' => $iQuota,
175: 'domain' => $sDomain
176: ]
177: );
178: $aParseResult = self::parseResponse($sCpanelResponse, false);
179: } catch(\Exception $oException) {
180: throw new ApiException(0, $oException, $oException->getMessage());
181: }
182: if (is_array($aParseResult) && isset($aParseResult['Data']) && !empty($aParseResult['Data'])) {
183: try {
184: $oAccount = \Aurora\Modules\Mail\Module::Decorator()->CreateAccount($oUser->Id, $sFriendlyName, $sLogin, $sLogin, $sPassword);
185: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount) {
186: $iTime = $bSignMe ? 0 : time();
187: $sAuthToken = \Aurora\System\Api::UserSession()->Set(
188: [
189: 'token' => 'auth',
190: 'sign-me' => $bSignMe,
191: 'id' => $oAccount->IdUser,
192: 'account' => $oAccount->Id,
193: 'account_type' => $oAccount->getName()
194: ],
195: $iTime
196: );
197: $mResult = ['AuthToken' => $sAuthToken];
198: }
199: } catch (\Exception $oException) {
200: if ($oException instanceof \Aurora\Modules\Mail\Exceptions\Exception &&
201: $oException->getCode() === \Aurora\Modules\Mail\Enums\ErrorCodes::CannotLoginCredentialsIncorrect) {
202: \Aurora\Modules\Core\Module::Decorator()->DeleteUser($oUser->Id);
203: }
204: throw $oException;
205: }
206: } elseif (is_array($aParseResult) && isset($aParseResult['Error'])) {
207: //If Account wasn't created - delete user
208: $bPrevState = \Aurora\System\Api::skipCheckUserRole(true);
209: \Aurora\Modules\Core\Module::Decorator()->DeleteUser($oUser->Id);
210: \Aurora\System\Api::skipCheckUserRole($bPrevState);
211: throw new \Exception($aParseResult['Error']);
212: }
213: }
214: }
215: }
216: \Aurora\System\Api::skipCheckUserRole($bPrevState);
217: }
218:
219: return true; // break subscriptions to prevent account creation in other modules
220: }
221:
222: /**
223: * Creates cPanel account before mail account will be created in the system.
224: * @param array $aArgs
225: * @param mixed $mResult
226: * @throws \Exception
227: */
228: public function onBeforeCreateAccount($aArgs, &$mResult)
229: {
230: $iUserId = $aArgs['UserId'];
231: $oUser = \Aurora\System\Api::getUserById($iUserId);
232: $sAccountEmail = $aArgs['Email'];
233: if ($this->isAliasExists($sAccountEmail, $oUser->IdTenant)) {
234: throw new \Exception($this->i18N('ERROR_EMAIL_MATCHES_ALIAS'));
235: }
236: if ($oUser instanceof \Aurora\Modules\Core\Models\User && $sAccountEmail === $oUser->PublicId) {
237: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sAccountEmail);
238: if (!empty($sDomain) && $this->isDomainSupported($sDomain)) {
239: $aTenantQuota = \Aurora\Modules\Mail\Module::Decorator()->GetEntitySpaceLimits('Tenant', $oUser->Id, $oUser->IdTenant);
240: $iQuota = $aTenantQuota ? $aTenantQuota['UserSpaceLimitMb'] : (int) $this->getConfig('UserDefaultQuotaMB', 1);
241: $oCpanel = $this->getCpanel($oUser->IdTenant);
242: $sCpanelResponse = $this->executeCpanelAction(
243: $oCpanel,
244: 'Email',
245: 'add_pop',
246: [
247: 'email' => $aArgs['IncomingLogin'],
248: 'password' => $aArgs['IncomingPassword'],
249: 'quota' => $iQuota,
250: 'domain' => $sDomain
251: ]
252: );
253: $aResult = self::parseResponse($sCpanelResponse, false);
254: if ($aResult['Status'] === false && isset($aResult['Error']) && !empty($aResult['Error']) && strrpos(strtolower($aResult['Error']), 'exists') === false) {
255: throw new \Exception($aResult['Error']);
256: }
257: }
258: }
259: }
260:
261: /**
262: * Sets flag that allows to delete mail account on cPanel.
263: * @param array $aArgs
264: * @param mixed $mResult
265: */
266: public function onBeforeDeleteEntities($aArgs, &$mResult)
267: {
268: if (isset($aArgs['DeletionConfirmedByAdmin']) && $aArgs['DeletionConfirmedByAdmin'] === true) {
269: self::$bAllowDeleteFromMailServerIfPossible = true;
270: }
271: }
272:
273: /**
274: * Deletes cPanel account, its aliases, forward, autoresponder and filters.
275: * @param array $aArgs
276: * @param mixed $mResult
277: */
278: public function onBeforeDeleteAccount($aArgs, &$mResult)
279: {
280: $oAccount = $aArgs['Account'];
281: $oUser = $aArgs['User'];
282: if ($oUser instanceof \Aurora\Modules\Core\Models\User
283: && $oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
284: && $this->isAccountServerSupported($oAccount)
285: && $oAccount->Email === $oUser->PublicId
286: ) {
287: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
288: if (!empty($sDomain)) {
289: $aAliases = self::Decorator()->GetAliases($oUser->Id);
290: if (isset($aAliases['Aliases']) && is_array($aAliases['Aliases']) && count($aAliases['Aliases']) > 0) {
291: self::Decorator()->DeleteAliases($oUser->Id, $aAliases['Aliases']);
292: }
293:
294: $aForwardArgs = [
295: 'AccountID' => $oAccount->Id,
296: 'Enable' => false,
297: 'Email' => '',
298: ];
299: $mForwardResult = false;
300: $this->onBeforeUpdateForward($aForwardArgs, $mForwardResult);
301:
302: $this->deleteAutoresponder($oAccount->Email, $oUser->IdTenant);
303:
304: $oSettings = $this->GetModuleSettings();
305: //Checking if an account exists on CPanel
306: $oCpanel = $this->getCpanel($oUser->IdTenant);
307: if ($oCpanel) {
308: $sCpanelResponse = $this->executeCpanelAction(
309: $oCpanel,
310: 'Email',
311: 'list_pops',
312: ['regex' => $oAccount->Email]
313: );
314: $aParseResult = self::parseResponse($sCpanelResponse);
315: //Trying to delete an Account only if it exists on CPanel
316: if (
317: $aParseResult
318: && isset($aParseResult['Data'])
319: && is_array($aParseResult['Data'])
320: && count($aParseResult['Data']) > 0
321: && $oSettings->GetValue('AllowCreateDeleteAccountOnCpanel', false)
322: && self::$bAllowDeleteFromMailServerIfPossible
323: ) {
324: $sCpanelResponse = $this->executeCpanelAction(
325: $oCpanel,
326: 'Email',
327: 'delete_pop',
328: [
329: 'email' => $oAccount->Email,
330: 'domain' => $sDomain,
331: ]
332: );
333: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
334: }
335: }
336: }
337: }
338: }
339:
340: /**
341: * Updates mail quota on cPanel.
342: * @param array $aArgs
343: * @param mixed $mResult
344: */
345: public function onUpdateQuota($aArgs, &$mResult)
346: {
347: $iTenantId = $aArgs['TenantId'];
348: $sEmail = $aArgs['Email'];
349: $iQuota = $aArgs['QuotaMb'];
350: $oCpanel = $this->getCpanel($iTenantId);
351: $sLogin = \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail);
352: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail);
353: if ($this->isDomainSupported($sDomain)) {
354: $sCpanelResponse = $this->executeCpanelAction(
355: $oCpanel,
356: 'Email',
357: 'edit_pop_quota',
358: [
359: 'email' => $sLogin,
360: 'domain' => $sDomain,
361: 'quota' => $iQuota
362: ]
363: );
364: $mResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
365: }
366: }
367:
368: public function onBeforeSendOrSaveMessage(&$aArgs, &$mResult)
369: {
370: $oUser = \Aurora\System\Api::getAuthenticatedUser();
371: $oAlias = $this->getManager('Aliases')->getAlias((int) $aArgs['AliasID']);
372: if ($oAlias instanceof \Aurora\Modules\CpanelIntegrator\Models\Alias
373: && $oUser instanceof \Aurora\Modules\Core\Models\User
374: && $oAlias->IdUser === $oUser->Id
375: ) {
376: $aArgs['Alias'] = $oAlias;
377: }
378: }
379:
380: /**
381: * Adds to account response array information about if it is allowed to change the password for this account.
382: * @param array $aArguments
383: * @param mixed $mResult
384: */
385: public function onMailAccountToResponseArray($aArguments, &$mResult)
386: {
387: $oAccount = $aArguments['Account'];
388:
389: if ($oAccount && $this->isAccountServerSupported($oAccount)) {
390: if (!isset($mResult['Extend']) || !is_array($mResult['Extend'])) {
391: $mResult['Extend'] = [];
392: }
393: $mResult['Extend']['AllowChangePasswordOnMailServer'] = true;
394:
395: $oMailModule = \Aurora\System\Api::GetModule('Mail');
396: if ($oMailModule) {
397: $mResult['AllowFilters'] = $oMailModule->getConfig('AllowFilters', '');
398: $mResult['AllowForward'] = $oMailModule->getConfig('AllowForward', '');
399: $mResult['AllowAutoresponder'] = $oMailModule->getConfig('AllowAutoresponder', '');
400: }
401:
402: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($oAccount->IdUser);
403: if ($oAccount->Email === $oUser->PublicId) {
404: try {
405: $aAliases = self::Decorator()->GetAliases($oAccount->IdUser);
406: $mResult['Extend']['Aliases'] = is_array($aAliases) && isset($aAliases['Aliases']) ? $aAliases['Aliases'] : [];
407: } catch (\Exception $oException) {
408: }
409: }
410: }
411: }
412:
413: /**
414: * Tries to change password for account if it is allowed.
415: * @param array $aArguments
416: * @param mixed $mResult
417: */
418: public function onChangeAccountPassword($aArguments, &$mResult)
419: {
420: $bPasswordChanged = false;
421: $bBreakSubscriptions = false;
422:
423: $oAccount = $aArguments['Account'];
424: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
425: && $this->isAccountServerSupported($oAccount)
426: && ($oAccount->getPassword() === $aArguments['CurrentPassword'] || isset($aArguments['SkipCurrentPasswordCheck']) && $aArguments['SkipCurrentPasswordCheck'])) {
427: $bPasswordChanged = $this->changePassword($oAccount, $aArguments['NewPassword']);
428: $bBreakSubscriptions = true; // break if Cpanel plugin tries to change password in this account.
429: }
430:
431: if (is_array($mResult)) {
432: $mResult['AccountPasswordChanged'] = $mResult['AccountPasswordChanged'] || $bPasswordChanged;
433: }
434:
435: return $bBreakSubscriptions;
436: }
437:
438: /**
439: * Updates forward for specified account.
440: * @param array $aArgs
441: * @param mixed $mResult
442: * @return boolean
443: * @throws \Aurora\System\Exceptions\ApiException
444: */
445: public function onBeforeUpdateForward($aArgs, &$mResult)
446: {
447: if (!isset($aArgs['AccountID']) || !isset($aArgs['Enable']) || !isset($aArgs['Email'])
448: || $aArgs['Enable'] && (empty(trim($aArgs['Email'])) || !filter_var(trim($aArgs['Email']), FILTER_VALIDATE_EMAIL))) {
449: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
450: }
451:
452: $sToEmail = trim($aArgs['Email']);
453:
454: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
455: $oAccount = \Aurora\System\Api::GetModule('Mail')->GetAccount($aArgs['AccountID']);
456: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount && $this->isAccountServerSupported($oAccount)) {
457: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
458: // check if account belongs to authenticated user
459: && ($oAccount->IdUser === $oAuthenticatedUser->Id
460: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin
461: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) {
462: $oCpanel = null;
463: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($oAccount->IdUser);
464: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
465: $oCpanel = $this->getCpanel($oUser->IdTenant);
466: }
467: if ($oCpanel) {
468: $sFromEmail = $oAccount->Email;
469: $sFromDomain = \MailSo\Base\Utils::GetDomainFromEmail($sFromEmail);
470:
471: // check if there is forwarder on cPanel
472: $aResult = $this->getForwarder($sFromDomain, $sFromEmail, $oUser->IdTenant);
473: $bForwarderExists = is_array($aResult) && isset($aResult['Email']) && !empty($aResult['Email']);
474: $sOldToEmail = $bForwarderExists ? $aResult['Email'] : '';
475: // "Enable" indicates if forwarder should exist on cPanel
476: if ($aArgs['Enable']) {
477: if ($bForwarderExists) {
478: $bSameForwarderExists = $bForwarderExists && $sOldToEmail === $sToEmail;
479: if ($bSameForwarderExists) {
480: $mResult = true;
481: } elseif ($this->deleteForwarder($sFromEmail, $sOldToEmail, $oUser->IdTenant)) {
482: $mResult = $this->createForwarder($sFromDomain, $sFromEmail, $sToEmail, $oUser->IdTenant);
483: }
484: } else {
485: $mResult = $this->createForwarder($sFromDomain, $sFromEmail, $sToEmail, $oUser->IdTenant);
486: }
487: } else {
488: if ($bForwarderExists) {
489: $mResult = $this->deleteForwarder($sFromEmail, $sOldToEmail, $oUser->IdTenant);
490: } else {
491: $mResult = true;
492: }
493: }
494: }
495: }
496:
497: return true; // breaking subscriptions to prevent update in parent module
498: }
499:
500: return false;
501: }
502:
503: /**
504: * Obtains forward data for specified account.
505: * @param array $aArgs
506: * @param mixed $mResult
507: * @return boolean
508: */
509: public function onBeforeGetForward($aArgs, &$mResult)
510: {
511: $mResult = false;
512:
513: if (isset($aArgs['AccountID'])) {
514: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
515: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($aArgs['AccountID']);
516: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
517: && $this->isAccountServerSupported($oAccount)) {
518: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
519: // check if account belongs to authenticated user
520: && $oAccount->IdUser === $oAuthenticatedUser->Id) {
521: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
522: $aResult = $this->getForwarder($sDomain, $oAccount->Email, $oAuthenticatedUser->IdTenant);
523:
524: if (is_array($aResult)) {
525: if (isset($aResult['Email'])) {
526: $mResult = [
527: 'Enable' => true,
528: 'Email' => $aResult['Email']
529: ];
530: } else {
531: $mResult = [
532: 'Enable' => false,
533: 'Email' => ''
534: ];
535: }
536: }
537: }
538: return true; // breaking subscriptions to prevent update in parent module
539: }
540: }
541:
542: return false;
543: }
544:
545: /**
546: * Obtains autoresponder for specified account.
547: * @param array $aArgs
548: * @param mixed $mResult
549: * @return boolean
550: */
551: public function onBeforeGetAutoresponder($aArgs, &$mResult)
552: {
553: $mResult = false;
554:
555: if (isset($aArgs['AccountID'])) {
556: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
557: $oAccount = \Aurora\System\Api::GetModule('Mail')->GetAccount($aArgs['AccountID']);
558: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
559: && $this->isAccountServerSupported($oAccount)) {
560: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
561: // check if account belongs to authenticated user
562: && $oAccount->IdUser === $oAuthenticatedUser->Id) {
563: $mAutoresponder = $this->getAutoresponder($oAccount->Email, $oAuthenticatedUser->IdTenant);
564:
565: if (is_array($mAutoresponder) && isset($mAutoresponder['Enable'])) {
566: $mResult = [
567: 'Enable' => $mAutoresponder['Enable'],
568: 'Subject' => $mAutoresponder['Subject'],
569: 'Message' => $mAutoresponder['Message']
570: ];
571: }
572: }
573: return true; // breaking subscriptions to prevent update in parent module
574: }
575: }
576:
577: return false;
578: }
579:
580: /**
581: * Updates autoresponder data.
582: * @param array $aArgs
583: * @param mixed $mResult
584: * @return boolean
585: * @throws \Aurora\System\Exceptions\ApiException
586: */
587: public function onBeforeUpdateAutoresponder($aArgs, &$mResult)
588: {
589: if (!isset($aArgs['AccountID']) || !isset($aArgs['Enable']) || !isset($aArgs['Subject']) || !isset($aArgs['Message'])
590: || $aArgs['Enable'] && (empty(trim($aArgs['Subject'])) || empty(trim($aArgs['Message'])))) {
591: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
592: }
593:
594: $mResult = false;
595:
596: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
597: $oAccount = \Aurora\System\Api::GetModule('Mail')->GetAccount($aArgs['AccountID']);
598: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
599: && $this->isAccountServerSupported($oAccount)) {
600: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
601: // check if account belongs to authenticated user
602: && $oAccount->IdUser === $oAuthenticatedUser->Id) {
603: $sSubject = trim($aArgs['Subject']);
604: $sMessage = trim($aArgs['Message']);
605: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
606: $mResult = $this->updateAutoresponder($sDomain, $oAccount->Email, $sSubject, $sMessage, $aArgs['Enable'], $oAuthenticatedUser->IdTenant);
607: }
608:
609: return true; // breaking subscriptions to prevent update in parent module
610: }
611:
612: return false;
613: }
614:
615: /**
616: * Obtains filters for specified account.
617: * @param array $aArgs
618: * @param mixed $mResult
619: * @return boolean
620: */
621: public function onBeforeGetFilters($aArgs, &$mResult)
622: {
623: if (!isset($aArgs['AccountID'])) {
624: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
625: }
626:
627: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($aArgs['AccountID']);
628: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
629: && $this->isAccountServerSupported($oAccount)) {
630: $mResult = [];
631:
632: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
633: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
634: // check if account belongs to authenticated user
635: && $oAccount->IdUser === $oAuthenticatedUser->Id) {
636: $oCpanel = $this->getCpanel($oAuthenticatedUser->IdTenant);
637: if ($oCpanel) {
638: $mResult = $this->getFilters($oAccount, $oAuthenticatedUser->IdTenant);
639: }
640: }
641:
642: return true; // breaking subscriptions to prevent update in parent module
643: }
644:
645: return false;
646: }
647:
648: /**
649: * Updates filters.
650: * @param array $aArgs
651: * @param mixed $mResult
652: * @return boolean
653: * @throws \Aurora\System\Exceptions\ApiException
654: */
655: public function onBeforeUpdateFilters($aArgs, &$mResult)
656: {
657: if (!isset($aArgs['AccountID']) || !isset($aArgs['Filters']) || !is_array($aArgs['Filters'])) {
658: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
659: }
660:
661: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($aArgs['AccountID']);
662: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
663: && $this->isAccountServerSupported($oAccount)) {
664: $mResult = false;
665:
666: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
667: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
668: // check if account belongs to authenticated user
669: && $oAccount->IdUser === $oAuthenticatedUser->Id) {
670: $oCpanel = $this->getCpanel($oAuthenticatedUser->IdTenant);
671: if ($oCpanel && $this->removeSupportedFilters($oAccount, $oAuthenticatedUser->IdTenant)) {
672: if (count($aArgs['Filters']) === 0) {
673: $mResult = true;
674: } else {
675: foreach ($aArgs['Filters'] as $aWebmailFilter) {
676: $aFilterProperty = self::convertWebmailFIlterToCPanelFIlter($aWebmailFilter, $oAccount);
677: //create filter
678: $sCpanelResponse = $this->executeCpanelAction(
679: $oCpanel,
680: 'Email',
681: 'store_filter',
682: $aFilterProperty
683: );
684: $aCreationResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
685: //disable filter if needed
686: if (!$aCreationResult['Status']) {
687: $mResult = false;
688: break;
689: }
690: if (!$aWebmailFilter['Enable']) {
691: $sCpanelResponse = $this->executeCpanelAction(
692: $oCpanel,
693: 'Email',
694: 'disable_filter',
695: [
696: 'account' => $oAccount->Email,
697: 'filtername' => $aFilterProperty['filtername']
698: ]
699: );
700: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
701: }
702: $mResult = true;
703: }
704: }
705: }
706: }
707:
708: return true; // breaking subscriptions to prevent update in parent module
709: }
710:
711: return false;
712: }
713:
714: public function onAfterIsEmailAllowedForCreation($aArgs, &$mResult)
715: {
716: if ($mResult && isset($aArgs['Email'])) {
717: $iTenantId = 0;
718: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserByPublicId($aArgs['Email']);
719: if ($oUser) {
720: $iTenantId = $oUser->IdTenant;
721: }
722: $mResult = !$this->isAliasExists($aArgs['Email'], $iTenantId);
723: }
724: }
725:
726: /**
727: * Checks if the CpanelIntegrator module can work with the specified domain.
728: * @param string $sDomain
729: * @return bool
730: */
731: protected function isDomainSupported($sDomain)
732: {
733: $bSupported = in_array('*', $this->getConfig('SupportedServers', array()));
734:
735: if (!$bSupported) {
736: $aGetMailServerResult = \Aurora\System\Api::GetModuleDecorator('Mail')->GetMailServerByDomain($sDomain, /*AllowWildcardDomain*/true);
737: if (!empty($aGetMailServerResult) && isset($aGetMailServerResult['Server']) && $aGetMailServerResult['Server'] instanceof \Aurora\Modules\Mail\Models\Server) {
738: $bSupported = in_array($aGetMailServerResult['Server']->IncomingServer, $this->getConfig('SupportedServers'));
739: }
740: }
741:
742: return $bSupported;
743: }
744:
745: /**
746: * Checks if the CpanelIntegrator module can work with the server of the specified account.
747: * @param \Aurora\Modules\Mail\Models\MailAccount $oAccount
748: * @return bool
749: */
750: protected function isAccountServerSupported($oAccount)
751: {
752: $bSupported = in_array('*', $this->getConfig('SupportedServers', array()));
753:
754: if (!$bSupported) {
755: $oServer = $oAccount->getServer();
756: if ($oServer instanceof \Aurora\Modules\Mail\Models\Server) {
757: $bSupported = in_array($oServer->IncomingServer, $this->getConfig('SupportedServers'));
758: }
759: }
760:
761: return $bSupported;
762: }
763:
764: /**
765: * Tries to change password for account.
766: * @param \Aurora\Modules\Mail\Models\MailAccount $oAccount
767: * @param string $sPassword
768: * @return boolean
769: * @throws \Aurora\System\Exceptions\ApiException
770: */
771: protected function changePassword($oAccount, $sPassword)
772: {
773: $bResult = false;
774:
775: if (0 < strlen($oAccount->IncomingPassword) && $oAccount->IncomingPassword !== $sPassword) {
776: $cpanel_host = $this->getConfig('CpanelHost', '');
777: $cpanel_user = $this->getConfig('CpanelUser', '');
778: $cpanel_pass = $this->getConfig('CpanelPassword', '');
779: $cpanel_user0 = null;
780:
781: $oUser = \Aurora\System\Api::getUserById($oAccount->IdUser);
782: $oTenant = $oUser instanceof \Aurora\Modules\Core\Models\User ? \Aurora\System\Api::getTenantById($oUser->IdTenant) : null;
783: if ($oTenant instanceof \Aurora\Modules\Core\Models\Tenant) {
784: $cpanel_host = $this->oModuleSettings->GetTenantValue($oTenant->Name, 'CpanelHost', '');
785: $cpanel_user = $this->oModuleSettings->GetTenantValue($oTenant->Name, 'CpanelUser', '');
786: $cpanel_pass = $this->oModuleSettings->GetTenantValue($oTenant->Name, 'CpanelPassword', '');
787: }
788:
789: $email_user = urlencode($oAccount->Email);
790: $email_pass = urlencode($sPassword);
791: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
792:
793: if ($cpanel_user == 'root') {
794: $query = 'https://' . $cpanel_host . ':2087/json-api/listaccts?api.version=1&searchtype=domain&search=' . $sDomain;
795:
796: $curl = curl_init();
797: curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
798: curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
799: curl_setopt($curl, CURLOPT_HEADER, 0);
800: curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
801: $header[0] = 'Authorization: Basic ' . base64_encode($cpanel_user . ':' . $cpanel_pass) . "\n\r";
802: curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
803: curl_setopt($curl, CURLOPT_URL, $query);
804: $result = curl_exec($curl);
805: if ($result == false) {
806: \Aurora\System\Api::Log('curl_exec threw error "' . curl_error($curl) . '" for ' . $query);
807: curl_close($curl);
808: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Exceptions\Errs::UserManager_AccountNewPasswordUpdateError);
809: } else {
810: curl_close($curl);
811: \Aurora\System\Api::Log('..:: QUERY0 ::.. ' . $query);
812: $json_res = json_decode($result, true);
813: \Aurora\System\Api::Log('..:: RESULT0 ::.. ' . $result);
814: if (isset($json_res['data']['acct'][0]['user'])) {
815: $cpanel_user0 = $json_res['data']['acct'][0]['user'];
816: \Aurora\System\Api::Log('..:: USER ::.. ' . $cpanel_user0);
817: }
818: }
819: $query = 'https://' . $cpanel_host . ':2087/json-api/cpanel?cpanel_jsonapi_user=' . $cpanel_user0 . '&cpanel_jsonapi_module=Email&cpanel_jsonapi_func=passwdpop&cpanel_jsonapi_apiversion=2&email=' . $email_user . '&password=' . $email_pass . '&domain=' . $sDomain;
820: } else {
821: $query = 'https://' . $cpanel_host . ':2083/execute/Email/passwd_pop?email=' . $email_user . '&password=' . $email_pass . '&domain=' . $sDomain;
822: }
823:
824: $curl = curl_init();
825: curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
826: curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
827: curl_setopt($curl, CURLOPT_HEADER, 0);
828: curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
829: $header[0] = 'Authorization: Basic ' . base64_encode($cpanel_user . ':' . $cpanel_pass) . "\n\r";
830: curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
831: curl_setopt($curl, CURLOPT_URL, $query);
832: $result = curl_exec($curl);
833: if ($result === false) {
834: \Aurora\System\Api::Log('curl_exec threw error "' . curl_error($curl) . '" for ' . $query);
835: curl_close($curl);
836: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Exceptions\Errs::UserManager_AccountNewPasswordUpdateError);
837: } else {
838: curl_close($curl);
839: \Aurora\System\Api::Log('..:: QUERY ::.. ' . $query);
840: $json_res = json_decode($result, true);
841: \Aurora\System\Api::Log('..:: RESULT ::.. ' . $result);
842: if ((isset($json_res['errors']))&&($json_res['errors']!==null)) {
843: $sErrorText = is_string($json_res['errors']) ? $json_res['errors'] : (is_array($json_res['errors']) && isset($json_res['errors'][0]) ? $json_res['errors'][0] : '');
844: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Exceptions\Errs::UserManager_AccountNewPasswordUpdateError, null, $sErrorText);
845: } else {
846: $bResult = true;
847: }
848: }
849: }
850:
851: return $bResult;
852: }
853:
854: /**
855: * Obtains forwarder from cPanel.
856: * @param string $sDomain
857: * @param string $sEmail
858: * @return array
859: */
860: protected function getForwarder($sDomain, $sEmail, $iTenantId = 0)
861: {
862: $aResult = [];
863:
864: $oCpanel = $this->getCpanel($iTenantId);
865: if ($oCpanel && $sDomain && $sEmail) {
866: $sCpanelResponse = $this->executeCpanelAction(
867: $oCpanel,
868: 'Email',
869: 'list_forwarders',
870: [
871: 'domain' => $sDomain,
872: 'regex' => '^' . $sEmail . '$'
873: ]
874: );
875: $aParseResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
876: $sForwardScriptPath = $this->getConfig('ForwardScriptPath', \dirname(__FILE__) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'process_mail.php');
877: if ($aParseResult
878: && isset($aParseResult['Data'])
879: && is_array($aParseResult['Data'])
880: && count($aParseResult['Data']) > 0
881: ) {
882: foreach ($aParseResult['Data'] as $oData) {
883: if ($oData->forward !== '|'.$sForwardScriptPath) {
884: $aResult = [
885: 'Email' => $oData->forward
886: ];
887: break;
888: }
889: }
890: }
891: }
892:
893: return $aResult;
894: }
895:
896: /**
897: * Deletes forwarder from cPanel.
898: * @param string $sAddress
899: * @param string $sForwarder
900: * @param int $iTenantId
901: * @return boolean
902: */
903: protected function deleteForwarder($sAddress, $sForwarder, $iTenantId = 0)
904: {
905: $oCpanel = $this->getCpanel($iTenantId);
906:
907: if ($oCpanel && $sAddress && $sForwarder) {
908: $sCpanelResponse = $this->executeCpanelAction(
909: $oCpanel,
910: 'Email',
911: 'delete_forwarder',
912: [
913: 'address' => $sAddress,
914: 'forwarder' => $sForwarder
915: ]
916: );
917: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
918: return true;
919: }
920:
921: return false;
922: }
923:
924: /**
925: * Creates forwarder on cPanel.
926: * @param string $sDomain
927: * @param string $sEmail
928: * @param string $sForwardEmail
929: * @param int $iTenantId
930: * @return boolean
931: */
932: protected function createForwarder($sDomain, $sEmail, $sForwardEmail, $iTenantId = 0)
933: {
934: $oCpanel = $this->getCpanel($iTenantId);
935:
936: if ($oCpanel && $sDomain && $sEmail && $sForwardEmail) {
937: $sCpanelResponse = $this->executeCpanelAction(
938: $oCpanel,
939: 'Email',
940: 'add_forwarder',
941: [
942: 'domain' => $sDomain,
943: 'email' => $sEmail,
944: 'fwdopt' => 'fwd',
945: 'fwdemail' => $sForwardEmail
946: ]
947: );
948: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
949: return true;
950: }
951:
952: return false;
953: }
954:
955: /**
956: * Obtains domain forwarders.
957: * @param string $sEmail
958: * @param string $sDomain
959: * @param int $iTenantId
960: * @return array
961: */
962: protected function getDomainForwarders($sEmail, $sDomain, $iTenantId = 0)
963: {
964: $oCpanel = $this->getCpanel($iTenantId);
965: if ($oCpanel && $sDomain && $sEmail) {
966: $sCpanelResponse = $this->executeCpanelAction(
967: $oCpanel,
968: 'Email',
969: 'list_forwarders',
970: [
971: 'domain' => $sDomain
972: ]
973: );
974: $aResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
975: if (is_array($aResult) && isset($aResult['Data'])) {
976: return $aResult['Data'];
977: }
978: }
979:
980: return [];
981: }
982:
983: /**
984: * Obtains autoresponder on cPanel.
985: * @param string $sEmail
986: * @return array|boolean
987: */
988: protected function getAutoresponder($sEmail, $iTenantId = 0)
989: {
990: $mResult = false;
991:
992: $oCpanel = $this->getCpanel($iTenantId);
993: if ($oCpanel && $sEmail) {
994: $sCpanelResponse = $this->executeCpanelAction(
995: $oCpanel,
996: 'Email',
997: 'get_auto_responder',
998: [
999: 'email' => $sEmail
1000: ]
1001: );
1002: $aParseResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1003: if ($aParseResult
1004: && isset($aParseResult['Data'])
1005: && isset($aParseResult['Data']->subject)
1006: ) {
1007: if ($aParseResult['Data']->stop !== null && $aParseResult['Data']->stop < time()) {
1008: $bEnable = false;
1009: } else {
1010: $bEnable = true;
1011: }
1012: $mResult = [
1013: 'Subject' => $aParseResult['Data']->subject,
1014: 'Message' => $aParseResult['Data']->body,
1015: 'Enable' => $bEnable
1016: ];
1017: }
1018: }
1019:
1020: return $mResult;
1021: }
1022:
1023: /**
1024: * Updates autoresponder on cPanel.
1025: * @param string $sDomain
1026: * @param string $sEmail
1027: * @param string $sSubject
1028: * @param string $sMessage
1029: * @param boolean $bEnable
1030: * @return array
1031: */
1032: protected function updateAutoresponder($sDomain, $sEmail, $sSubject, $sMessage, $bEnable, $iTenantId = 0)
1033: {
1034: $aResult = [
1035: 'Status' => false
1036: ];
1037:
1038: $oCpanel = $this->getCpanel($iTenantId);
1039: if ($oCpanel && $sDomain && $sEmail && $sSubject && $sMessage) {
1040: $iStartTime = 0;
1041: $iStopTime = 0;
1042: if (!$bEnable) {
1043: $iStopTime = time();
1044: $iStartTime = $iStopTime - 1;
1045: }
1046: $sCpanelResponse = $this->executeCpanelAction(
1047: $oCpanel,
1048: 'Email',
1049: 'add_auto_responder',
1050: [
1051: 'email' => $sEmail,
1052: 'from' => '',
1053: 'subject' => $sSubject,
1054: 'body' => $sMessage,
1055: 'domain' => $sDomain,
1056: 'is_html' => 0,
1057: 'interval' => 8,
1058: 'start' => $iStartTime,
1059: 'stop' => $iStopTime
1060: ]
1061: );
1062: $aResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1063: }
1064:
1065: return $aResult;
1066: }
1067:
1068: /**
1069: * Deletes autoresponder from cPanel.
1070: * @param string $sEmail
1071: */
1072: protected function deleteAutoresponder($sEmail, $iTenantId = 0)
1073: {
1074: $oCpanel = $this->getCpanel($iTenantId);
1075: if ($oCpanel && $sEmail) {
1076: $sCpanelResponse = $this->executeCpanelAction(
1077: $oCpanel,
1078: 'Email',
1079: 'delete_auto_responder',
1080: [
1081: 'email' => $sEmail,
1082: ]
1083: );
1084: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1085: }
1086: }
1087:
1088: /**
1089: * Obtains filters from cPanel.
1090: * @param object $oAccount
1091: * @return array
1092: */
1093: protected function getFilters($oAccount, $iTenantId = 0)
1094: {
1095: $aFilters = [];
1096:
1097: $oCpanel = $this->getCpanel($iTenantId);
1098: if ($oCpanel && $oAccount) {
1099: $sCpanelResponse = $this->executeCpanelAction(
1100: $oCpanel,
1101: 'Email',
1102: 'list_filters',
1103: [
1104: 'account' => $oAccount->Email
1105: ]
1106: );
1107: $aParseResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1108: if ($aParseResult && isset($aParseResult['Data'])) {
1109: $aFilters = self::convertCPanelFIltersToWebmailFIlters($aParseResult['Data'], $oAccount);
1110: }
1111: }
1112:
1113: return $aFilters;
1114: }
1115:
1116: /**
1117: * Removes filters supported by the system from cPanel.
1118: * @param object $oAccount
1119: * @return boolean
1120: */
1121: protected function removeSupportedFilters($oAccount, $iTenantId = 0)
1122: {
1123: $bResult = false;
1124:
1125: if ($oAccount) {
1126: $aFilters = $this->getFilters($oAccount, $iTenantId);
1127: if (is_array($aFilters)) {
1128: if (count($aFilters) === 0) {
1129: $bResult = true;
1130: } else {
1131: $aSuportedFilterNames = array_map(function ($aFilter) {
1132: return $aFilter['Filtername'];
1133: }, $aFilters);
1134:
1135: $oCpanel = $this->getCpanel($iTenantId);
1136: if ($oCpanel) {
1137: $bDelResult = true;
1138: foreach ($aSuportedFilterNames as $sSuportedFilterName) {
1139: $sCpanelResponse = $this->executeCpanelAction(
1140: $oCpanel,
1141: 'Email',
1142: 'delete_filter',
1143: [
1144: 'account' => $oAccount->Email,
1145: 'filtername' => $sSuportedFilterName
1146: ]
1147: );
1148: $aDelResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1149: if (!$aDelResult['Status']) {
1150: $bDelResult = false;
1151: break;
1152: }
1153: }
1154: $bResult = $bDelResult;
1155: }
1156: }
1157: }
1158: }
1159:
1160: return $bResult;
1161: }
1162:
1163: /**
1164: * Obtains namespace for mail account from IMAP.
1165: * @staticvar object $oNamespace
1166: * @param object $oAccount
1167: * @return object
1168: */
1169: protected static function getImapNamespace($oAccount)
1170: {
1171: static $oNamespace = null;
1172:
1173: if ($oNamespace === null) {
1174: $oNamespace = \Aurora\System\Api::GetModule('Mail')->getMailManager()->_getImapClient($oAccount)->GetNamespace();
1175: }
1176:
1177: return $oNamespace;
1178: }
1179:
1180: /**
1181: * Parses response from cPanel.
1182: * @param string $sResponse
1183: * @param boolean $bAllowException
1184: * @return array
1185: * @throws \Exception
1186: */
1187: protected static function parseResponse($sResponse, $bAllowException = true)
1188: {
1189: $aResult = [
1190: 'Status' => false
1191: ];
1192:
1193: $oResult = \json_decode($sResponse);
1194:
1195: if ($oResult
1196: && isset($oResult->result)
1197: && isset($oResult->result->status)
1198: && $oResult->result->status === 1
1199: ) {
1200: $aResult = [
1201: 'Status' => true,
1202: 'Data' => $oResult->result->data
1203: ];
1204: } elseif ($oResult && isset($oResult->error)) {
1205: $aResult = [
1206: 'Status' => false,
1207: 'Error' => $oResult->error
1208: ];
1209: } elseif ($oResult && isset($oResult->result)
1210: && isset($oResult->result->errors) && !empty($oResult->result->errors)
1211: && isset($oResult->result->errors[0])) {
1212: $aResult = [
1213: 'Status' => false,
1214: 'Error' => $oResult->result->errors[0]
1215: ];
1216: } else {
1217: $aResult = [
1218: 'Status' => false,
1219: 'Error' => $sResponse
1220: ];
1221: }
1222:
1223: if ($bAllowException && $aResult['Status'] === false) {
1224: throw new \Exception(trim($aResult['Error'], ' ()'));
1225: }
1226:
1227: return $aResult;
1228: }
1229:
1230: /**
1231: * Converts cPanel filters to webmail filters.
1232: * @param array $aCPanelFilters
1233: * @param object $oAccount
1234: * @return array
1235: */
1236: protected static function convertCPanelFIltersToWebmailFIlters($aCPanelFilters, $oAccount)
1237: {
1238: $aResult = [];
1239:
1240: foreach ($aCPanelFilters as $oCPanelFilter) {
1241: $iAction = null;
1242: $iCondition = null;
1243: $iField = null;
1244:
1245: if ($oCPanelFilter->actions[0]->action === 'save') {
1246: if ($oCPanelFilter->actions[0]->dest === '/dev/null') {
1247: $iAction = \Aurora\Modules\Mail\Enums\FilterAction::DeleteFromServerImmediately;
1248: } else {
1249: $iAction = \Aurora\Modules\Mail\Enums\FilterAction::MoveToFolder;
1250: }
1251: }
1252:
1253: $sDestEmail = "";
1254: if ($oCPanelFilter->actions[0]->action === 'deliver') {
1255: $sDestEmail = $oCPanelFilter->actions[0]->dest;
1256: $iAction =\Aurora\Modules\Mail\Enums\FilterAction::Redirect;
1257: }
1258:
1259: switch ($oCPanelFilter->rules[0]->match) {
1260: case 'contains':
1261: $iCondition = \Aurora\Modules\Mail\Enums\FilterCondition::ContainSubstring;
1262: break;
1263: case 'does not contain':
1264: $iCondition = \Aurora\Modules\Mail\Enums\FilterCondition::NotContainSubstring;
1265: break;
1266: case 'is':
1267: $iCondition = \Aurora\Modules\Mail\Enums\FilterCondition::ContainExactPhrase;
1268: break;
1269: }
1270:
1271: switch ($oCPanelFilter->rules[0]->part) {
1272: case '$header_from:':
1273: $iField = \Aurora\Modules\Mail\Enums\FilterFields::From;
1274: break;
1275: case '$header_to:':
1276: $iField = \Aurora\Modules\Mail\Enums\FilterFields::To;
1277: break;
1278: case '$header_subject:':
1279: $iField = \Aurora\Modules\Mail\Enums\FilterFields::Subject;
1280: break;
1281: }
1282: $oNamespace = self::getImapNamespace($oAccount);
1283:
1284: $sNamespace = \str_replace($oNamespace->GetPersonalNamespaceDelimiter(), '', $oNamespace->GetPersonalNamespace());
1285:
1286: $aFolderNameParts = \explode('/', $oCPanelFilter->actions[0]->dest);
1287: $sFolderFullName = '';
1288: if (\count($aFolderNameParts) > 1 && $aFolderNameParts[\count($aFolderNameParts) - 1] !== $sNamespace) {
1289: $sFolderFullName = $sNamespace . $aFolderNameParts[\count($aFolderNameParts) - 1];
1290: }
1291:
1292: if (isset($iAction) && isset($iCondition) && isset($iField)
1293: && (!empty($sFolderFullName) || $iAction === \Aurora\Modules\Mail\Enums\FilterAction::DeleteFromServerImmediately || $iAction === \Aurora\Modules\Mail\Enums\FilterAction::Redirect)
1294: ) {
1295: $aResult[] = [
1296: 'Action' => $iAction,
1297: 'Condition' => $iCondition,
1298: 'Enable' => (bool) $oCPanelFilter->enabled,
1299: 'Field' => $iField,
1300: 'Filter' => $oCPanelFilter->rules[0]->val,
1301: 'FolderFullName' => $iAction === \Aurora\Modules\Mail\Enums\FilterAction::DeleteFromServerImmediately ? '' : $sFolderFullName,
1302: 'Filtername' => $oCPanelFilter->filtername,
1303: 'Email' => $sDestEmail
1304: ];
1305: }
1306: }
1307:
1308: return $aResult;
1309: }
1310:
1311: /**
1312: * Converts webmail filters to cPanel filters.
1313: * @param array $aWebmailFilter
1314: * @param object $oAccount
1315: * @return array
1316: */
1317: protected static function convertWebmailFIlterToCPanelFIlter($aWebmailFilter, $oAccount)
1318: {
1319: $sAction = '';
1320: $sPart = '';
1321: $sMatch = '';
1322: $oNamespace = self::getImapNamespace($oAccount);
1323: $sNamespace = \str_replace($oNamespace->GetPersonalNamespaceDelimiter(), '', $oNamespace->GetPersonalNamespace());
1324: $sDest = \str_replace($sNamespace, '/', $aWebmailFilter["FolderFullName"]);
1325:
1326: switch ($aWebmailFilter["Action"]) {
1327: case \Aurora\Modules\Mail\Enums\FilterAction::DeleteFromServerImmediately:
1328: $sDest = '/dev/null';
1329: // no break
1330: case \Aurora\Modules\Mail\Enums\FilterAction::MoveToFolder:
1331: $sAction = 'save';
1332: break;
1333: case \Aurora\Modules\Mail\Enums\FilterAction::Redirect:
1334: $sAction = 'deliver';
1335: $sDest = isset($aWebmailFilter['Email']) ? $aWebmailFilter['Email'] : $sDest;
1336: break;
1337: }
1338:
1339: switch ($aWebmailFilter["Condition"]) {
1340: case \Aurora\Modules\Mail\Enums\FilterCondition::ContainSubstring:
1341: $sMatch = 'contains';
1342: break;
1343: case \Aurora\Modules\Mail\Enums\FilterCondition::NotContainSubstring:
1344: $sMatch = 'does not contain';
1345: break;
1346: case \Aurora\Modules\Mail\Enums\FilterCondition::ContainExactPhrase:
1347: $sMatch = 'is';
1348: break;
1349: }
1350:
1351: switch ($aWebmailFilter["Field"]) {
1352: case \Aurora\Modules\Mail\Enums\FilterFields::From:
1353: $sPart = '$header_from:';
1354: break;
1355: case \Aurora\Modules\Mail\Enums\FilterFields::To:
1356: $sPart = '$header_to:';
1357: break;
1358: case \Aurora\Modules\Mail\Enums\FilterFields::Subject:
1359: $sPart = '$header_subject:';
1360: break;
1361: }
1362:
1363: return [
1364: 'filtername' => \uniqid(),
1365: 'account' => $oAccount->Email,
1366: 'action1' => $sAction,
1367: 'dest1' => $sDest,
1368: 'part1' => $sPart,
1369: 'match1' => $sMatch,
1370: 'val1' => $aWebmailFilter['Filter'],
1371: 'opt1' => 'or',
1372: ];
1373: }
1374:
1375: /**
1376: * Obtains list of module settings for authenticated user.
1377: * @return array
1378: */
1379: public function GetSettings($TenantId = null)
1380: {
1381: $oSettings = $this->GetModuleSettings();
1382:
1383: if (empty($TenantId)) {
1384: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1385: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::NormalUser) {
1386: return [
1387: 'AllowAliases' => $oSettings->GetValue('AllowAliases', false)
1388: ];
1389: } elseif ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin) {
1390: return [
1391: 'AllowAliases' => $oSettings->GetValue('AllowAliases', false),
1392: 'AllowCreateDeleteAccountOnCpanel' => $oSettings->GetValue('AllowCreateDeleteAccountOnCpanel', false),
1393: ];
1394: }
1395: }
1396:
1397: if (!empty($TenantId)) {
1398: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
1399: $oTenant = \Aurora\System\Api::getTenantById($TenantId);
1400:
1401: if ($oTenant instanceof \Aurora\Modules\Core\Models\Tenant) {
1402: return [
1403: 'CpanelHost' => $oSettings->GetTenantValue($oTenant->Name, 'CpanelHost', ''),
1404: 'CpanelPort' => $oSettings->GetTenantValue($oTenant->Name, 'CpanelPort', ''),
1405: 'CpanelUser' => $oSettings->GetTenantValue($oTenant->Name, 'CpanelUser', ''),
1406: 'CpanelHasPassword' => $oSettings->GetTenantValue($oTenant->Name, 'CpanelPassword', '') !== '',
1407: ];
1408: }
1409: }
1410:
1411: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
1412: return [
1413: 'CpanelHost' => $oSettings->GetValue('CpanelHost', ''),
1414: 'CpanelPort' => $oSettings->GetValue('CpanelPort', ''),
1415: 'CpanelUser' => $oSettings->GetValue('CpanelUser', ''),
1416: 'CpanelHasPassword' => $oSettings->GetValue('CpanelPassword', '') !== '',
1417: 'AllowAliases' => $oSettings->GetValue('AllowAliases', false),
1418: 'AllowCreateDeleteAccountOnCpanel' => $oSettings->GetValue('AllowCreateDeleteAccountOnCpanel', false),
1419: ];
1420: }
1421:
1422: /**
1423: * Updates module's settings - saves them to config.json file or to user settings in db.
1424: * @param int $ContactsPerPage Count of contacts per page.
1425: * @return boolean
1426: */
1427: public function UpdateSettings($CpanelHost, $CpanelPort, $CpanelUser, $CpanelPassword, $TenantId = null)
1428: {
1429: $result = false;
1430: $oSettings = $this->GetModuleSettings();
1431: if (!empty($TenantId)) {
1432: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
1433: $oTenant = \Aurora\System\Api::getTenantById($TenantId);
1434:
1435: if ($oTenant) {
1436: $oSettings->SetTenantValue($oTenant->Name, 'CpanelHost', $CpanelHost);
1437: $oSettings->SetTenantValue($oTenant->Name, 'CpanelPort', $CpanelPort);
1438: $oSettings->SetTenantValue($oTenant->Name, 'CpanelUser', $CpanelUser);
1439: if ($CpanelPassword !== '') {
1440: $oSettings->SetTenantValue($oTenant->Name, 'CpanelPassword', $CpanelPassword);
1441: }
1442: $result = $oSettings->SaveTenantSettings($oTenant->Name);
1443: }
1444: } else {
1445: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
1446:
1447: $oSettings->SetValue('CpanelHost', $CpanelHost);
1448: $oSettings->SetValue('CpanelPort', $CpanelPort);
1449: $oSettings->SetValue('CpanelUser', $CpanelUser);
1450: if ($CpanelPassword !== '') {
1451: $oSettings->SetValue('CpanelPassword', $CpanelPassword);
1452: }
1453: $result = $oSettings->Save();
1454: }
1455:
1456: return $result;
1457: }
1458:
1459: /**
1460: * Obtains all aliases for specified user.
1461: * @param int $UserId User identifier.
1462: * @return array|boolean
1463: */
1464: public function GetAliases($UserId)
1465: {
1466: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1467:
1468: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($UserId);
1469: $bUserFound = $oUser instanceof \Aurora\Modules\Core\Models\User;
1470: if ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::NormalUser && $oUser->Id === $oAuthenticatedUser->Id) {
1471: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
1472: } elseif ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin && $oUser->IdTenant === $oAuthenticatedUser->IdTenant) {
1473: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
1474: } else {
1475: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
1476: }
1477: $oAccount = \Aurora\System\Api::GetModuleDecorator('Mail')->GetAccountByEmail($oUser->PublicId, $oUser->Id);
1478: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount && $this->isAccountServerSupported($oAccount)) {
1479: $aForwardersFromEmail = [];
1480: $sEmail = $oAccount->Email;
1481: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail);
1482:
1483: $aAliases = $this->getManager('Aliases')->getAliasesByUserId($oUser->Id);
1484: $oMailModule = \Aurora\System\Api::GetModule('Mail');
1485: //is Server Supported
1486: $oServer = $oMailModule->getServersManager()->getServer($oAccount->ServerId);
1487: $bSupported = in_array($oServer->IncomingServer, $this->getConfig('SupportedServers'));
1488: if ($bSupported) {
1489: $aServerDomainsByTenant = $oMailModule::Decorator()->GetServerDomains($oAccount->ServerId, $oUser->IdTenant);
1490: //Get forwarders for all supported domains
1491: $aForwarders = [];
1492: foreach ($aServerDomainsByTenant as $sServerDomain) {
1493: $aForwarders = array_merge($aForwarders, $this->getDomainForwarders($sEmail, $sServerDomain, $oUser->IdTenant));
1494: }
1495:
1496: //filter forvarders
1497: $aFilteredForwarders = $this->getAliasesFromForwarders($aForwarders, $oUser->IdTenant);
1498: foreach ($aFilteredForwarders as $oForwarder) {
1499: $sFromEmail = $oForwarder->dest;
1500: $sToEmail = $oForwarder->forward;
1501: if ($sToEmail === $sEmail) {
1502: $aForwardersFromEmail[] = $sFromEmail;
1503: }
1504: }
1505: $aAliasesEmail = $aAliases->map(function ($oAlias) {
1506: return $oAlias->Email;
1507: })->toArray();
1508:
1509: foreach ($aForwardersFromEmail as $sForwarderFromEmail) {
1510: if (!in_array($sForwarderFromEmail, $aAliasesEmail)) {
1511: //Check if an alias exists
1512: $aAliasesCheck = $this->getManager('Aliases')->getAliases(0, 0, Models\Alias::where('Email', $sForwarderFromEmail));
1513: if (empty($aAliasesCheck)) {
1514: //create alias if doesn't exists
1515: $oAlias = new \Aurora\Modules\CpanelIntegrator\Models\Alias();
1516: $oAlias->IdUser = $oUser->Id;
1517: $oAlias->IdAccount = $oAccount->Id;
1518: $oAlias->Email = $sForwarderFromEmail;
1519: $oAlias->ForwardTo = $oAccount->Email;
1520: $this->getManager('Aliases')->createAlias($oAlias);
1521: }
1522: }
1523: }
1524: foreach ($aAliases as $oAlias) {
1525: if (!in_array($oAlias->Email, $aForwardersFromEmail)) {
1526: $this->getManager('Aliases')->deleteAlias($oAlias);
1527: }
1528: }
1529: $aAliases = $this->getManager('Aliases')->getAliasesByUserId($oUser->Id);
1530: }
1531:
1532: return [
1533: 'Domain' => $sDomain,
1534: 'Aliases' => $aForwardersFromEmail,
1535: 'ObjAliases' => $aAliases->toArray()
1536: ];
1537: }
1538:
1539: return false;
1540: }
1541:
1542: /**
1543: * Creates new alias with specified name and domain.
1544: * @param int $UserId User identifier.
1545: * @param string $AliasName Alias name.
1546: * @param string $AliasDomain Alias domain.
1547: * @return boolean|int
1548: */
1549: public function AddNewAlias($UserId, $AliasName, $AliasDomain)
1550: {
1551: $AliasName = strtolower($AliasName); // cPanel creates an alias with a lowercase name.
1552: $mResult = false;
1553: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1554:
1555: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($UserId);
1556: $bUserFound = $oUser instanceof \Aurora\Modules\Core\Models\User;
1557: if ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::NormalUser && $UserId === $oAuthenticatedUser->Id) {
1558: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
1559: } elseif ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin && $oUser->IdTenant === $oAuthenticatedUser->IdTenant) {
1560: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
1561: } else {
1562: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
1563: }
1564:
1565: $oMailDecorator = \Aurora\System\Api::GetModuleDecorator('Mail');
1566: $oAccount = $bUserFound && $oMailDecorator ? $oMailDecorator->GetAccountByEmail($oUser->PublicId, $oUser->Id) : null;
1567: $aServerDomainsByTenant = $oMailDecorator ? $oMailDecorator->GetServerDomains($oAccount->ServerId, $oUser->IdTenant) : [];
1568: //AliasDomain must be in the tenant’s domain list
1569: if (!in_array($AliasDomain, $aServerDomainsByTenant)) {
1570: throw new \Aurora\System\Exceptions\ApiException(Enums\ErrorCodes::DomainOutsideTenant);
1571: }
1572: //Checking if an alias matches an existing account
1573: $oCpanel = $this->getCpanel($oUser->IdTenant);
1574: if ($oCpanel) {
1575: $sCpanelResponse = $this->executeCpanelAction($oCpanel, 'Email', 'list_pops', []);
1576: $aParseResult = self::parseResponse($sCpanelResponse);
1577: if ($aParseResult && isset($aParseResult['Data'])) {
1578: $aEmailAccountsOnCPanel = array_map(function ($oAccount) {
1579: return $oAccount->email;
1580: }, $aParseResult['Data']);
1581: if (in_array($AliasName . '@' . $AliasDomain, $aEmailAccountsOnCPanel)) {
1582: throw new \Aurora\System\Exceptions\ApiException(Enums\ErrorCodes::AliaMatchesExistingEmail);
1583: }
1584: }
1585: }
1586: //Check if an alias exists on CPanel
1587: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
1588: $aResult = $this->getForwarder($AliasDomain, $AliasName . '@' . $AliasDomain, $oUser->IdTenant);
1589: $bAliasExists = is_array($aResult) && isset($aResult['Email']) && !empty($aResult['Email']);
1590: if ($bAliasExists) {
1591: throw new \Aurora\System\Exceptions\ApiException(Enums\ErrorCodes::AliasAlreadyExists);
1592: }
1593: //Check if an alias exists
1594: $aAliases = $this->getManager('Aliases')->getAliases(0, 0, Models\Alias::where('Email', $AliasName . '@' . $AliasDomain));
1595: if ($aAliases->count() > 0) {
1596: throw new \Aurora\System\Exceptions\ApiException(Enums\ErrorCodes::AliasAlreadyExists);
1597: }
1598:
1599: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
1600: && $this->isAccountServerSupported($oAccount)) {
1601: $sEmail = $oAccount->Email;
1602: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oAccount->Email);
1603: $aServerDomainsByTenant = $oMailDecorator ? $oMailDecorator->GetServerDomains($oAccount->ServerId, $oUser->IdTenant) : [];
1604: //Get forwarders for all supported domains
1605: $aForwarders = [];
1606: foreach ($aServerDomainsByTenant as $sServerDomain) {
1607: $aForwarders = array_merge($aForwarders, $this->getDomainForwarders($sEmail, $sServerDomain, $oUser->IdTenant));
1608: }
1609: $aDomainAliases = $this->getAliasesFromForwarders($aForwarders, $oUser->IdTenant);
1610: $aUserAliases = [];
1611: foreach ($aDomainAliases as $oAlias) {
1612: $sToEmail = $oAlias->forward;
1613: if ($sToEmail === $sEmail) {
1614: $aUserAliases[] = $oAlias;
1615: }
1616: }
1617: $aArgs = [
1618: 'TenantId' => $oUser->IdTenant,
1619: 'DomainAliases' => $aDomainAliases,
1620: 'UserAliases' => $aUserAliases,
1621: 'AliasName' => $AliasName,
1622: 'AliasDomain' => $AliasDomain,
1623: 'ToEmail' => $oAccount->Email,
1624: 'UserId' => $oUser->Id
1625: ];
1626: $this->broadcastEvent(
1627: 'CreateAlias::before',
1628: $aArgs
1629: );
1630: $bCreateForwardewResult = $this->createForwarder($AliasDomain, $AliasName . '@' . $AliasDomain, $oAccount->Email, $oUser->IdTenant);
1631: if ($bCreateForwardewResult) {
1632: //create Alias
1633: $oAlias = new \Aurora\Modules\CpanelIntegrator\Models\Alias();
1634: $oAlias->IdUser = $oUser->Id;
1635: $oAlias->IdAccount = $oAccount->Id;
1636: $oAlias->Email = $AliasName . '@' . $AliasDomain;
1637: $oAlias->ForwardTo = $oAccount->Email;
1638: $mResult = $this->getManager('Aliases')->createAlias($oAlias);
1639: }
1640: }
1641:
1642: return $mResult;
1643: }
1644:
1645: /**
1646: * Update existing Alias
1647: *
1648: * @param int $UserId
1649: * @param int $AccountID
1650: * @param stryng $FriendlyName
1651: * @param int $EntityId
1652: * @return bool
1653: * @throws \Aurora\System\Exceptions\ApiException
1654: */
1655: public function UpdateAlias($UserId, $AccountID, $FriendlyName, $EntityId)
1656: {
1657: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
1658:
1659: $bResult = false;
1660: $oUser = \Aurora\System\Api::getAuthenticatedUser();
1661: $oMailDecorator = \Aurora\System\Api::GetModuleDecorator('Mail');
1662: $oAccount = $oMailDecorator ? $oMailDecorator->GetAccount($AccountID) : null;
1663: if (!$oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount || $oAccount->IdUser !== $oUser->Id) {
1664: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::InvalidInputParameter);
1665: }
1666: $oAlias = $this->getManager('Aliases')->getAlias((int) $EntityId);
1667: if ($oAlias instanceof \Aurora\Modules\CpanelIntegrator\Models\Alias
1668: && $oAlias->IdUser === $oUser->Id
1669: ) {
1670: $oAlias->FriendlyName = $FriendlyName;
1671: $bResult = $this->getManager('Aliases')->updateAlias($oAlias);
1672: }
1673:
1674: return $bResult;
1675: }
1676:
1677: /**
1678: * Deletes aliases with specified emails.
1679: * @param int $UserId User identifier
1680: * @param array $Aliases Aliases emails.
1681: * @return boolean
1682: */
1683: public function DeleteAliases($UserId, $Aliases)
1684: {
1685: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1686:
1687: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($UserId);
1688: $bUserFound = $oUser instanceof \Aurora\Modules\Core\Models\User;
1689:
1690: if ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::NormalUser && $oUser->Id === $oAuthenticatedUser->Id) {
1691: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
1692: } elseif ($bUserFound && $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin && $oUser->IdTenant === $oAuthenticatedUser->IdTenant) {
1693: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::TenantAdmin);
1694: } else {
1695: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::SuperAdmin);
1696: }
1697:
1698: $bResult = false;
1699: $oMailDecorator = \Aurora\System\Api::GetModuleDecorator('Mail');
1700: $oAccount = $bUserFound && $oMailDecorator ? $oMailDecorator->GetAccountByEmail($oUser->PublicId, $oUser->Id) : null;
1701: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount
1702: && $this->isAccountServerSupported($oAccount)) {
1703: foreach ($Aliases as $sAlias) {
1704: preg_match('/(.+)@(.+)$/', $sAlias, $aMatches);
1705: $AliasName = isset($aMatches[1]) ? $aMatches[1] : '';
1706: $AliasDomain = isset($aMatches[2]) ? $aMatches[2] : '';
1707: $bResult = $this->deleteForwarder($AliasName . '@' . $AliasDomain, $oAccount->Email, $oUser->IdTenant);
1708: if ($bResult) {
1709: $oAlias = $this->getManager('Aliases')->getUserAliasByEmail($oUser->Id, $AliasName . '@' . $AliasDomain);
1710: if ($oAlias instanceof \Aurora\Modules\CpanelIntegrator\Models\Alias) {
1711: $this->getManager('Aliases')->deleteAlias($oAlias);
1712: }
1713: }
1714: }
1715: }
1716:
1717: return $bResult;
1718: }
1719:
1720: public function UpdateSignature($UserId, $AliasId = null, $UseSignature = null, $Signature = null)
1721: {
1722: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
1723:
1724: $bResult = false;
1725: $oUser = \Aurora\System\Api::getAuthenticatedUser();
1726: if ($oUser->Id === $UserId) {
1727: $oAlias = $this->getManager('Aliases')->getAlias((int) $AliasId);
1728: if ($oAlias instanceof \Aurora\Modules\CpanelIntegrator\Models\Alias && $oAlias->IdUser === $oUser->Id) {
1729: $oAlias->UseSignature = $UseSignature;
1730: $oAlias->Signature = $Signature;
1731: $bResult = $this->getManager('Aliases')->updateAlias($oAlias);
1732: }
1733: }
1734:
1735: return $bResult;
1736: }
1737:
1738: protected function getAliasesFromForwarders($aForwarders, $iTenantId = 0)
1739: {
1740: $aResult = [];
1741: $oCpanel = $this->getCpanel($iTenantId);
1742: if ($oCpanel) {
1743: $sCpanelResponse = $this->executeCpanelAction($oCpanel, 'Email', 'list_pops', []);
1744: $aParseResult = self::parseResponse($sCpanelResponse);
1745: if ($aParseResult && isset($aParseResult['Data'])) {
1746: $aEmailAccountsOnCPanel = array_map(function ($oAccount) {
1747: return $oAccount->email;
1748: }, $aParseResult['Data']);
1749: foreach ($aForwarders as &$oForwarder) {
1750: //if 'dest' is the email address of the real account, it is not an alias
1751: if (!in_array($oForwarder->dest, $aEmailAccountsOnCPanel)) {
1752: $aResult[] = $oForwarder;
1753: }
1754: }
1755: }
1756: }
1757:
1758: return $aResult;
1759: }
1760:
1761: protected function isAliasExists($sEmail, $iTenantId = 0)
1762: {
1763: //Check if an alias exists on CPanel
1764: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($sEmail);
1765: $aResult = $this->getForwarder($sDomain, $sEmail, $iTenantId);
1766: $bAliasExists = is_array($aResult) && isset($aResult['Email']) && !empty($aResult['Email']);
1767:
1768: return $bAliasExists;
1769: }
1770:
1771: public function GetScriptForward($AccountID)
1772: {
1773: $aResult = [];
1774:
1775: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1776: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($AccountID);
1777: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount && $this->isAccountServerSupported($oAccount)) {
1778: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
1779: // check if account belongs to authenticated user
1780: && ($oAccount->IdUser === $oAuthenticatedUser->Id
1781: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin
1782: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) {
1783: $oCpanel = null;
1784: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($oAccount->IdUser);
1785: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
1786: $oCpanel = $this->getCpanel($oUser->IdTenant);
1787: $sEmail = $oAccount->Email;
1788: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oUser->PublicId);
1789: $sForwardScriptPath = $this->getConfig('ForwardScriptPath', \dirname(__FILE__) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'process_mail.php');
1790: if ($oCpanel && $sDomain && $sEmail && !empty($sForwardScriptPath)) {
1791: $sCpanelResponse = $this->executeCpanelAction(
1792: $oCpanel,
1793: 'Email',
1794: 'list_forwarders',
1795: [
1796: 'domain' => $sDomain,
1797: 'regex' => '^' . $sEmail . '$'
1798: ]
1799: );
1800: $aParseResult = self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1801: if ($aParseResult
1802: && isset($aParseResult['Data'])
1803: && is_array($aParseResult['Data'])
1804: && count($aParseResult['Data']) > 0
1805: ) {
1806: foreach ($aParseResult['Data'] as $oData) {
1807: if ($oData->forward === '|'.$sForwardScriptPath) {
1808: $aResult = [
1809: 'Email' => $oData->forward
1810: ];
1811: break;
1812: }
1813: }
1814: }
1815: } else {
1816: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Exceptions\Errs::Validation_InvalidParameters);
1817: }
1818: }
1819: }
1820: }
1821:
1822: return $aResult;
1823: }
1824:
1825: public function CreateScriptForward($AccountID)
1826: {
1827: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1828: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($AccountID);
1829: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount && $this->isAccountServerSupported($oAccount)) {
1830: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
1831: // check if account belongs to authenticated user
1832: && ($oAccount->IdUser === $oAuthenticatedUser->Id
1833: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin
1834: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) {
1835: $oCpanel = null;
1836: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($oAccount->IdUser);
1837: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
1838: $oCpanel = $this->getCpanel($oUser->IdTenant);
1839: $sEmail = $oAccount->Email;
1840: $sDomain = \MailSo\Base\Utils::GetDomainFromEmail($oUser->PublicId);
1841: $sForwardScriptPath = $this->getConfig('ForwardScriptPath', \dirname(__FILE__) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'process_mail.php');
1842:
1843: if ($oCpanel && $sDomain && $sEmail && !empty($sForwardScriptPath)) {
1844: $sCpanelResponse = $this->executeCpanelAction(
1845: $oCpanel,
1846: 'Email',
1847: 'add_forwarder',
1848: [
1849: 'domain' => $sDomain,
1850: 'email' => $sEmail,
1851: 'fwdopt' => 'pipe',
1852: 'pipefwd' => $sForwardScriptPath
1853: ]
1854: );
1855: self::parseResponse($sCpanelResponse); // throws exception in case if error has occured
1856: return true;
1857: } else {
1858: throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Exceptions\Errs::Validation_InvalidParameters);
1859: }
1860: }
1861: }
1862: }
1863:
1864: return false;
1865: }
1866:
1867: public function RemoveScriptForward($AccountID)
1868: {
1869: $bResult = false;
1870: $oAuthenticatedUser = \Aurora\System\Api::getAuthenticatedUser();
1871: $oAccount = \Aurora\Modules\Mail\Module::getInstance()->GetAccount($AccountID);
1872: if ($oAccount instanceof \Aurora\Modules\Mail\Models\MailAccount && $this->isAccountServerSupported($oAccount)) {
1873: if ($oAuthenticatedUser instanceof \Aurora\Modules\Core\Models\User
1874: // check if account belongs to authenticated user
1875: && ($oAccount->IdUser === $oAuthenticatedUser->Id
1876: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin
1877: || $oAuthenticatedUser->Role === \Aurora\System\Enums\UserRole::TenantAdmin)) {
1878: $sFromEmail = $oAccount->Email;
1879: $oUser = \Aurora\Modules\Core\Module::Decorator()->GetUserUnchecked($oAccount->IdUser);
1880: if ($oUser instanceof \Aurora\Modules\Core\Models\User) {
1881: $sForwardScriptPath = $this->getConfig('ForwardScriptPath', \dirname(__FILE__) . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'process_mail.php');
1882: if (!empty($sForwardScriptPath)) {
1883: try {
1884: $this->deleteForwarder($sFromEmail, '|' . $sForwardScriptPath, $oUser->IdTenant);
1885: $bResult = true;
1886: } catch (\Exception $oEx) {
1887: $bResult = false;
1888: }
1889: }
1890: }
1891: }
1892: }
1893:
1894: return $bResult;
1895: }
1896: }
1897: