/home/ivoiecob/email.hirewise-va.com/modules/OpenPgpWebclient/Module.php
<?php
/**
* This code is licensed under AGPLv3 license or Afterlogic Software License
* if commercial version of the product was purchased.
* For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
*/
namespace Aurora\Modules\OpenPgpWebclient;
use Aurora\Modules\Contacts\Enums\StorageType;
use Aurora\Modules\Contacts\Classes\Contact;
use Aurora\Modules\Contacts\Enums\Access;
use Aurora\Modules\Contacts\Models\ContactCard;
use Aurora\System\Api;
use Aurora\System\Enums\UserRole;
use Aurora\System\Exceptions\ApiException;
use Aurora\System\Notifications;
/**
* @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
* @license https://afterlogic.com/products/common-licensing Afterlogic Software License
* @copyright Copyright (c) 2023, Afterlogic Corp.
*
* @property Settings $oModuleSettings
*
* @package Modules
*/
class Module extends \Aurora\System\Module\AbstractWebclientModule
{
/**
* @return Module
*/
public static function getInstance()
{
return parent::getInstance();
}
/**
* @return Module
*/
public static function Decorator()
{
return parent::Decorator();
}
/**
* @return Settings
*/
public function getModuleSettings()
{
return $this->oModuleSettings;
}
public function init()
{
$this->subscribeEvent('Files::PopulateFileItem::after', array($this, 'onAfterPopulateFileItem'));
$this->subscribeEvent('Mail::GetBodyStructureParts', array($this, 'onGetBodyStructureParts'));
$this->subscribeEvent('Mail::ExtendMessageData', array($this, 'onExtendMessageData'));
$this->subscribeEvent('Contacts::CreateContact::after', array($this, 'onAfterCreateOrUpdateContact'));
$this->subscribeEvent('Contacts::UpdateContact::after', array($this, 'onAfterCreateOrUpdateContact'));
$this->subscribeEvent('Contacts::GetContacts::after', array($this, 'onAfterGetContacts'));
$this->subscribeEvent('Contacts::GetContactsByUids::after', array($this, 'onAfterGetContactsByUids'));
$this->subscribeEvent('System::CastExtendedProp', array($this, 'onCastExtendedProp'));
}
/**
* @ignore
* @todo not used
* @param array $aArgs
* @param object $oItem
*/
public function onAfterPopulateFileItem($aArgs, &$oItem)
{
if ($oItem && '.asc' === \strtolower(\substr(\trim($oItem->Name), -4))) {
if (class_exists('\Aurora\Modules\Files\Module')) {
$oFilesDecorator = \Aurora\Modules\Files\Module::Decorator();
if ($oFilesDecorator) {
$mResult = $oFilesDecorator->GetFileContent($aArgs['UserId'], $oItem->TypeStr, $oItem->Path, $oItem->Name);
if (isset($mResult)) {
$oItem->Content = $mResult;
}
}
}
}
}
public function onGetBodyStructureParts($aParts, &$aResultParts)
{
foreach ($aParts as $oPart) {
if ($oPart instanceof \MailSo\Imap\BodyStructure && $oPart->ContentType() === 'text/plain' && '.asc' === \strtolower(\substr(\trim($oPart->FileName()), -4))) {
$aResultParts[] = $oPart;
}
}
}
public function onExtendMessageData($aData, &$oMessage)
{
foreach ($aData as $aDataItem) {
$oPart = $aDataItem['Part'];
$bAsc = $oPart instanceof \MailSo\Imap\BodyStructure && $oPart->ContentType() === 'text/plain' && '.asc' === \strtolower(\substr(\trim($oPart->FileName()), -4));
$sData = $aDataItem['Data'];
if ($bAsc) {
$iMimeIndex = $oPart->PartID();
foreach ($oMessage->getAttachments()->GetAsArray() as $oAttachment) {
if ($iMimeIndex === $oAttachment->getMimeIndex()) {
$oAttachment->setContent($sData);
}
}
}
}
}
public function onAfterCreateOrUpdateContact($aArgs, &$mResult)
{
if (isset($mResult['UUID']) && isset($aArgs['Contact']['PublicPgpKey'])) {
$sPublicPgpKey = $aArgs['Contact']['PublicPgpKey'];
if (empty(\trim($sPublicPgpKey))) {
$sPublicPgpKey = null;
}
$oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($mResult['UUID'], $aArgs['UserId']);
if ($oContact instanceof Contact) {
$needsToUpdate = false;
if (isset($sPublicPgpKey)) {
$oContact->setExtendedProp($this->GetName() . '::PgpKey', $sPublicPgpKey);
$needsToUpdate = true;
} elseif ($oContact->getExtendedProp($this->GetName() . '::PgpKey')) {
$oContact->unsetExtendedProp($this->GetName() . '::PgpKey');
$needsToUpdate = true;
}
if (isset($aArgs['Contact']['PgpEncryptMessages']) && is_bool($aArgs['Contact']['PgpEncryptMessages'])) {
if ($aArgs['Contact']['Storage'] !== StorageType::Team) {
if ($oContact->getExtendedProp($this->GetName() . '::PgpEncryptMessages') !== $aArgs['Contact']['PgpEncryptMessages']) {
$oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages', $aArgs['Contact']['PgpEncryptMessages']);
$needsToUpdate = true;
}
} elseif ($oContact->getExtendedProp($this->GetName() . '::PgpEncryptMessages_' . $aArgs['UserId']) !== $aArgs['Contact']['PgpEncryptMessages']) {
$oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages_' . $aArgs['UserId'], $aArgs['Contact']['PgpEncryptMessages']);
$needsToUpdate = true;
}
}
if (isset($aArgs['Contact']['PgpSignMessages']) && is_bool($aArgs['Contact']['PgpSignMessages'])) {
if ($aArgs['Contact']['Storage'] !== StorageType::Team) {
if ($oContact->getExtendedProp($this->GetName() . '::PgpSignMessages') !== $aArgs['Contact']['PgpSignMessages']) {
$oContact->setExtendedProp($this->GetName() . '::PgpSignMessages', $aArgs['Contact']['PgpSignMessages']);
$needsToUpdate = true;
}
} elseif ($oContact->getExtendedProp($this->GetName() . '::PgpSignMessages_' . $aArgs['UserId']) !== $aArgs['Contact']['PgpSignMessages']) {
$oContact->setExtendedProp($this->GetName() . '::PgpSignMessages_' . $aArgs['UserId'], $aArgs['Contact']['PgpSignMessages']);
$needsToUpdate = true;
}
}
if ($needsToUpdate) {
$oContact->saveExtendedProps();
}
if (is_array($mResult) && isset($mResult['ETag'])) {
$mResult['ETag'] = $oContact->ETag;
}
}
}
}
/**
* The function copies values of user-related properties to the properties with original names.
* Then it removes the user-related flags cause they should be exposed to a user.
*/
public function onAfterGetContacts($aArgs, &$mResult)
{
if (isset($aArgs['UserId']) && isset($mResult['List']) && count($mResult['List']) > 0) {
$aContactUUIDs = array_map(function ($aValue) { return $aValue['UUID']; }, $mResult['List']);
$aContactCards = ContactCard::whereIn('CardId', $aContactUUIDs)->whereNotNull('Properties->' . $this->GetName() . '::PgpKey')->get();
$aContactCardsSorted = array();
$sEncryptPropName = $this->GetName() . '::PgpEncryptMessages';
$sSignPropName = $this->GetName() . '::PgpSignMessages';
foreach ($aContactCards as $oContactCard) {
if ($oContactCard->getExtendedProp($this->GetName() . '::PgpKey') !== '') {
$aContactCardsSorted[$oContactCard->CardId] = $oContactCard;
}
}
foreach ($mResult['List'] as &$aContact) {
$aContact['HasPgpPublicKey'] = false;
$aContact['PgpEncryptMessages'] = false;
$aContact['PgpSignMessages'] = false;
if (isset($aContactCardsSorted[$aContact['UUID']])) {
$oContactCard = $aContactCardsSorted[$aContact['UUID']];
$aContact['HasPgpPublicKey'] = true;
if (!empty($aContact['IsTeam'])) {
$aContact['PgpEncryptMessages'] = (bool) $oContactCard->getExtendedProp($sEncryptPropName . '_' . $aArgs['UserId'], false);
$aContact['PgpSignMessages'] = (bool) $oContactCard->getExtendedProp($sSignPropName . '_' . $aArgs['UserId'], false);
} else {
$aContact['PgpEncryptMessages'] = (bool) $oContactCard->getExtendedProp($sEncryptPropName, false);
$aContact['PgpSignMessages'] = (bool) $oContactCard->getExtendedProp($sSignPropName, false);
}
}
// remove OpenPGPWebclient properties
if (isset($aContact['Properties'])) {
foreach ($aContact['Properties'] as $sPropName => $sPropValue) {
if (strpos($sPropName, $this->GetName() . '::') !== false) {
unset($aContact['Properties'][$sPropName]);
}
}
}
}
}
}
/**
* The function copies values of user-related properties to the properties with original names.
* Then it removes the user-related flags cause they should be exposed to a user.
*/
public function onAfterGetContactsByUids($aArgs, &$mResult)
{
if (isset($aArgs['UserId']) && isset($mResult)) {
foreach ($mResult as $oContact) {
if ($oContact instanceof Contact) {
// add a srting property if it's missing or null
if (!$oContact->getExtendedProp($this->GetName() . '::PgpKey')) {
$oContact->setExtendedProp($this->GetName() . '::PgpKey', '');
}
if ($oContact->Storage === StorageType::Team) {
$sEncryptPropName = $this->GetName() . '::PgpEncryptMessages';
$sSignPropName = $this->GetName() . '::PgpSignMessages';
// copy user-related values to main properties
$oContact->setExtendedProp($sEncryptPropName, $oContact->getExtendedProp($sEncryptPropName . '_' . $aArgs['UserId']) || false);
$oContact->setExtendedProp($sSignPropName, $oContact->getExtendedProp($sSignPropName . '_' . $aArgs['UserId']) || false);
// remove user-related values from properties
foreach ($oContact->Properties as $sPropName => $sPropValue) {
if (strpos($sPropName, $sEncryptPropName . '_') !== false || strpos($sPropName, $sSignPropName . '_') !== false) {
$oContact->unsetExtendedProp($sPropName);
}
}
}
}
}
}
}
public function onCastExtendedProp($aArgs, &$mValue)
{
if ($aArgs['Model'] instanceof ContactCard &&
($aArgs['PropertyName'] === $this->GetName() . '::PgpEncryptMessages' ||
$aArgs['PropertyName'] === $this->GetName() . '::PgpSignMessages')) {
$mValue = (bool) $mValue;
}
}
/***** public functions might be called with web API *****/
/**
* Obtains list of module settings for authenticated user.
*
* @return array
*/
public function GetSettings()
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
$aSettings = [
'EnableModule' => false,
'RememberPassphrase' => false
];
$oUser = Api::getAuthenticatedUser();
if ($oUser && $oUser->isNormalOrTenant()) {
if (null !== $oUser->getExtendedProp(self::GetName() . '::EnableModule')) {
$aSettings['EnableModule'] = $oUser->getExtendedProp(self::GetName() . '::EnableModule');
}
if (null !== $oUser->getExtendedProp(self::GetName() . '::RememberPassphrase')) {
$aSettings['RememberPassphrase'] = $oUser->getExtendedProp(self::GetName() . '::RememberPassphrase');
}
}
return $aSettings;
}
public function UpdateSettings($EnableModule, $RememberPassphrase)
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oUser = Api::getAuthenticatedUser();
if ($oUser) {
if ($oUser->isNormalOrTenant()) {
$oCoreDecorator = \Aurora\Modules\Core\Module::Decorator();
$oUser->setExtendedProp(self::GetName() . '::EnableModule', $EnableModule);
if (isset($RememberPassphrase)) {
$oUser->setExtendedProp(self::GetName() . '::RememberPassphrase', $RememberPassphrase);
}
return $oCoreDecorator->UpdateUserObject($oUser);
}
if ($oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) {
return true;
}
}
return false;
}
/**
* Summary of AddPublicKeyToContactWithUUID
* @param int $UserId
* @param mixed $UUID
* @param string $Key
* @throws \Aurora\System\Exceptions\ApiException
* @return bool
*/
public function AddPublicKeyToContactWithUUID($UserId, $UUID, $Key)
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$contact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($UUID, $UserId);
if ($contact instanceof Contact) {
$user = Api::getUserById($UserId);
if (!\Aurora\Modules\Contacts\Module::Decorator()->CheckAccessToObject($user, $contact, Access::Write)) {
throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::AccessDenied);
}
if (\Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($contact)) {
$contact->setExtendedProp($this->GetName() . '::PgpKey', $Key);
return !!$contact->saveExtendedProps();
}
}
return false;
}
public function AddPublicKeyToContact($UserId, $Email, $Key, $UserName = '')
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$bResult = false;
$aUpdatedContactIds = [];
if (\MailSo\Base\Validator::SimpleEmailString($Email)) {
$aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails(
$UserId,
StorageType::Personal,
[$Email]
);
if (count($aContacts) === 0) {
$mResult = \Aurora\Modules\Contacts\Module::Decorator()->CreateContact(
[
'PersonalEmail' => $Email,
'FullName' => $UserName,
'Storage' => StorageType::Personal
],
$UserId
);
if (isset($mResult['UUID'])) {
$oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($mResult['UUID'], $UserId);
if ($oContact instanceof Contact) {
$aContacts = [$oContact];
}
}
}
if ($aContacts && count($aContacts) > 0) {
foreach ($aContacts as $oContact) {
$properties = $oContact->getExtendedProps();
$properties[$this->GetName() . '::PgpKey'] = $Key;
ContactCard::where('CardId', $oContact->Id)->update(['Properties' => $properties]);
$aUpdatedContactIds[] = $oContact->Id;
}
}
}
return $aUpdatedContactIds;
}
public function AddPublicKeysToContacts($UserId, $Keys)
{
$mResult = false;
$aUpdatedContactIds = [];
foreach ($Keys as $aKey) {
if (isset($aKey['Email'], $aKey['Key'])) {
$sUserName = isset($aKey['Name']) ? $aKey['Name'] : '';
$mResult = $this->AddPublicKeyToContact($UserId, $aKey['Email'], $aKey['Key'], $sUserName);
if (is_array($mResult)) {
$aUpdatedContactIds = array_merge($aUpdatedContactIds, $mResult);
}
}
}
return $aUpdatedContactIds;
}
public function RemovePublicKeyFromContact($UserId, $Email)
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$bResult = false;
if (\MailSo\Base\Validator::SimpleEmailString($Email)) {
$aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails(
$UserId,
StorageType::All,
[$Email]
);
if ($aContacts && count($aContacts) > 0) {
foreach ($aContacts as $oContact) {
if ($oContact instanceof ContactCard && !$oContact->IsTeam && !$oContact->Shared) {
$properties = $oContact->getExtendedProps();
$properties[$this->GetName() . '::PgpKey'] = null;
ContactCard::where('CardId', $oContact->Id)->update(['Properties' => $properties]);
}
}
$bResult = true;
}
}
return $bResult;
}
/**
* The method returns list of PGP public keys with their ViewEmails for specified contacts.
*
* @param int $UserId User ID. Provided automatically by WebAPI wrapper.
* @param array $ContactUUIDs List of contact IDs.
*
* @return array
*/
public function GetPublicKeysByCountactUUIDs($UserId, $ContactUUIDs)
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$aResult = [];
if (count($ContactUUIDs)) {
$aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByUids($UserId, $ContactUUIDs);
if (is_array($aContacts) && count($aContacts) > 0) {
foreach ($aContacts as $oContact) {
$aResult[] = [
'Email' => $oContact->ViewEmail,
'PublicPgpKey' => $oContact->getExtendedProp($this->GetName() . '::PgpKey')
];
}
}
}
return $aResult;
}
public function GetPublicKeysFromContacts($UserId)
{
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$aResult = [];
$aContactsInfo = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsInfo(
StorageType::All,
$UserId,
ContactCard::whereNotNull('Properties->' . $this->GetName() . '::PgpKey')
);
$aContactUUIDs = [];
if (is_array($aContactsInfo['Info']) && count($aContactsInfo['Info']) > 0) {
$aContactUUIDs = array_map(function ($aValue) {
if (!$aValue['IsTeam'] && !$aValue['Shared']) {
return $aValue['UUID'];
}
}, $aContactsInfo['Info']);
}
$aResult = $this->Decorator()->GetPublicKeysByCountactUUIDs($UserId, $aContactUUIDs);
return $aResult;
}
protected function updatePublicKeyFlags($UserId, $oContact, $PgpEncryptMessages = false, $PgpSignMessages = false)
{
$mResult = false;
if (class_exists('\Aurora\Modules\TeamContacts\Module')) {
$oTeamContactsDecorator = \Aurora\Modules\TeamContacts\Module::Decorator();
if ($oTeamContactsDecorator && $oContact instanceof Contact) {
$properties = $oContact->getExtendedProps();
$addressbook = $oTeamContactsDecorator->GetTeamAddressbook($UserId);
if ($addressbook && $oContact->AddressBookId == $addressbook['id']) {
$properties[$this->GetName() . '::PgpEncryptMessages_' . $UserId] = $PgpEncryptMessages;
$properties[$this->GetName() . '::PgpSignMessages_' . $UserId] = $PgpSignMessages;
} else {
$properties[$this->GetName() . '::PgpEncryptMessages'] = $PgpEncryptMessages;
$properties[$this->GetName() . '::PgpSignMessages'] = $PgpSignMessages;
}
ContactCard::where('CardId', $oContact->Id)->update(['Properties' => $properties]);
$mResult = true;
}
}
return $mResult;
}
public function UpdateContactPublicKeyFlags($UserId, $UUID, $PgpEncryptMessages = false, $PgpSignMessages = false)
{
$oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($UUID, $UserId);
$mResult = $this->updatePublicKeyFlags($UserId, $oContact, $PgpEncryptMessages, $PgpSignMessages);
return $mResult;
}
protected function getTeamContactByUser($oUser)
{
$mResult = false;
if (Api::GetModuleManager()->IsAllowedModule('TeamContacts')) {
$aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails(
$oUser->Id,
\Aurora\Modules\Contacts\Enums\StorageType::Team,
[$oUser->PublicId]
);
if ($aContacts && count($aContacts) > 0) {
$oContact = $aContacts[0];
if ($oContact instanceof ContactCard) {
$mResult = $oContact;
}
}
}
return $mResult;
}
public function UpdateOwnContactPublicKey($UserId, $PublicPgpKey = '')
{
$mResult = false;
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oUser = Api::getAuthenticatedUser();
if ($oUser) {
if ($oUser->Id === $UserId) {
$oContactCard = $this->getTeamContactByUser($oUser);
if ($oContactCard instanceof ContactCard) {
$properties = $oContactCard->Properties;
if (!empty($PublicPgpKey)) {
$properties[$this->GetName() . '::PgpKey'] = $PublicPgpKey;
} else {
unset($properties[$this->GetName() . '::PgpKey']);
}
$mResult = !!ContactCard::where('CardId', $oContactCard->Id)->update(['Properties' => $properties]);
}
}
}
return $mResult;
}
public function GetOwnContactPublicKey($UserId)
{
$mResult = false;
Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
$oUser = Api::getAuthenticatedUser();
if ($oUser) {
if ($oUser->Id === $UserId) {
$oContactCard = $this->getTeamContactByUser($oUser);
if ($oContactCard instanceof ContactCard) {
$mResult = $oContactCard->getExtendedProp($this->GetName() . '::PgpKey', false);
}
}
}
return $mResult;
}
/**
*
* @param int $UserId
* @param string $Content
* @param string $FileName
* @return array|bool
* @throws ApiException
*/
public function SaveKeyAsTempFile($UserId, $Content, $FileName)
{
$mResult = false;
Api::checkUserRoleIsAtLeast(UserRole::NormalUser);
$ext = '';
$fileInfo = pathinfo($FileName);
if (isset($fileInfo['extension'])) {
$ext = strtolower($fileInfo['extension']);
}
if ($ext !== 'asc') {
throw new ApiException(Notifications::FilesNotAllowed);
}
$sUUID = Api::getUserUUIDById($UserId);
try {
$sTempName = md5($sUUID . $Content . $FileName);
$oApiFileCache = new \Aurora\System\Managers\Filecache();
if (!$oApiFileCache->isFileExists($sUUID, $sTempName)) {
$oApiFileCache->put($sUUID, $sTempName, $Content);
}
if ($oApiFileCache->isFileExists($sUUID, $sTempName)) {
$mResult = \Aurora\System\Utils::GetClientFileResponse(
null,
$UserId,
$FileName,
$sTempName,
$oApiFileCache->fileSize($sUUID, $sTempName)
);
}
} catch (\Exception $oException) {
throw new ApiException(Notifications::FilesNotAllowed, $oException, $oException->getMessage());
}
return $mResult;
}
/***** public functions might be called with web API *****/
}