1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | namespace Aurora\Modules\OpenPgpWebclient; |
9: | |
10: | use Aurora\Modules\Contacts\Enums\StorageType; |
11: | use Aurora\Modules\Contacts\Models\Contact; |
12: | use Aurora\System\Api; |
13: | use Aurora\System\Enums\UserRole; |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | class Module extends \Aurora\System\Module\AbstractWebclientModule |
23: | { |
24: | public function init() |
25: | { |
26: | $this->subscribeEvent('Files::PopulateFileItem::after', array($this, 'onAfterPopulateFileItem')); |
27: | $this->subscribeEvent('Mail::GetBodyStructureParts', array($this, 'onGetBodyStructureParts')); |
28: | $this->subscribeEvent('Mail::ExtendMessageData', array($this, 'onExtendMessageData')); |
29: | $this->subscribeEvent('Contacts::CreateContact::after', array($this, 'onAfterCreateOrUpdateContact')); |
30: | $this->subscribeEvent('Contacts::UpdateContact::after', array($this, 'onAfterCreateOrUpdateContact')); |
31: | $this->subscribeEvent('Contacts::GetContacts::after', array($this, 'onAfterGetContacts')); |
32: | $this->subscribeEvent('System::CastExtendedProp', array($this, 'onCastExtendedProp')); |
33: | } |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | |
41: | public function onAfterPopulateFileItem($aArgs, &$oItem) |
42: | { |
43: | if ($oItem && '.asc' === \strtolower(\substr(\trim($oItem->Name), -4))) { |
44: | $oFilesDecorator = \Aurora\System\Api::GetModuleDecorator('Files'); |
45: | if ($oFilesDecorator instanceof \Aurora\System\Module\Decorator) { |
46: | $mResult = $oFilesDecorator->GetFileContent($aArgs['UserId'], $oItem->TypeStr, $oItem->Path, $oItem->Name); |
47: | if (isset($mResult)) { |
48: | $oItem->Content = $mResult; |
49: | } |
50: | } |
51: | } |
52: | } |
53: | |
54: | public function onGetBodyStructureParts($aParts, &$aResultParts) |
55: | { |
56: | foreach ($aParts as $oPart) { |
57: | if ($oPart instanceof \MailSo\Imap\BodyStructure && $oPart->ContentType() === 'text/plain' && '.asc' === \strtolower(\substr(\trim($oPart->FileName()), -4))) { |
58: | $aResultParts[] = $oPart; |
59: | } |
60: | } |
61: | } |
62: | |
63: | public function onExtendMessageData($aData, &$oMessage) |
64: | { |
65: | foreach ($aData as $aDataItem) { |
66: | $oPart = $aDataItem['Part']; |
67: | $bAsc = $oPart instanceof \MailSo\Imap\BodyStructure && $oPart->ContentType() === 'text/plain' && '.asc' === \strtolower(\substr(\trim($oPart->FileName()), -4)); |
68: | $sData = $aDataItem['Data']; |
69: | if ($bAsc) { |
70: | $iMimeIndex = $oPart->PartID(); |
71: | foreach ($oMessage->getAttachments()->GetAsArray() as $oAttachment) { |
72: | if ($iMimeIndex === $oAttachment->getMimeIndex()) { |
73: | $oAttachment->setContent($sData); |
74: | } |
75: | } |
76: | } |
77: | } |
78: | } |
79: | |
80: | public function onAfterCreateOrUpdateContact($aArgs, &$mResult) |
81: | { |
82: | if (isset($mResult['UUID']) && isset($aArgs['Contact']['PublicPgpKey'])) { |
83: | $sPublicPgpKey = $aArgs['Contact']['PublicPgpKey']; |
84: | if (empty(\trim($sPublicPgpKey))) { |
85: | $sPublicPgpKey = null; |
86: | } |
87: | $oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($mResult['UUID'], $aArgs['UserId']); |
88: | if ($oContact instanceof Contact) { |
89: | if (isset($sPublicPgpKey)) { |
90: | $oContact->setExtendedProp($this->GetName() . '::PgpKey', $sPublicPgpKey); |
91: | } else { |
92: | $oContact->unsetExtendedProp($this->GetName() . '::PgpKey'); |
93: | } |
94: | if (isset($aArgs['Contact']['PgpEncryptMessages']) && is_bool($aArgs['Contact']['PgpEncryptMessages'])) { |
95: | if ($aArgs['Contact']['Storage'] !== StorageType::Team) { |
96: | $oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages', $aArgs['Contact']['PgpEncryptMessages']); |
97: | } else { |
98: | $oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages_' . $aArgs['UserId'], $aArgs['Contact']['PgpEncryptMessages']); |
99: | } |
100: | } |
101: | if (isset($aArgs['Contact']['PgpSignMessages']) && is_bool($aArgs['Contact']['PgpSignMessages'])) { |
102: | if ($aArgs['Contact']['Storage'] !== StorageType::Team) { |
103: | $oContact->setExtendedProp($this->GetName() . '::PgpSignMessages', $aArgs['Contact']['PgpSignMessages']); |
104: | } else { |
105: | $oContact->setExtendedProp($this->GetName() . '::PgpSignMessages_' . $aArgs['UserId'], $aArgs['Contact']['PgpSignMessages']); |
106: | } |
107: | } |
108: | \Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($oContact); |
109: | if (is_array($mResult) && isset($mResult['ETag'])) { |
110: | $mResult['ETag'] = $oContact->ETag; |
111: | } |
112: | } |
113: | } |
114: | } |
115: | |
116: | public function onAfterGetContacts($aArgs, &$mResult) |
117: | { |
118: | if (isset($mResult['List'])) { |
119: | $aContactUUIDs = array_map(function ($aValue) { |
120: | return $aValue['UUID']; |
121: | }, $mResult['List']); |
122: | $aContactsInfo = $this->GetContactsWithPublicKeys($aArgs['UserId'], $aContactUUIDs); |
123: | foreach ($mResult['List'] as &$aContact) { |
124: | $aContact['HasPgpPublicKey'] = false; |
125: | $aContact['PgpEncryptMessages'] = false; |
126: | $aContact['PgpSignMessages'] = false; |
127: | if (isset($aContactsInfo[$aContact['UUID']])) { |
128: | $aContact['HasPgpPublicKey'] = true; |
129: | $aContact['PgpEncryptMessages'] = (bool) $aContactsInfo[$aContact['UUID']]['PgpEncryptMessages']; |
130: | $aContact['PgpSignMessages'] = (bool) $aContactsInfo[$aContact['UUID']]['PgpSignMessages']; |
131: | } |
132: | } |
133: | } |
134: | } |
135: | |
136: | public function onCastExtendedProp($aArgs, &$mValue) |
137: | { |
138: | if ($aArgs['Model'] instanceof Contact && |
139: | ($aArgs['PropertyName'] === $this->GetName() . '::PgpEncryptMessages' || |
140: | $aArgs['PropertyName'] === $this->GetName() . '::PgpSignMessages')) { |
141: | $mValue = (bool) $mValue; |
142: | } |
143: | } |
144: | |
145: | |
146: | |
147: | |
148: | |
149: | |
150: | |
151: | public function GetSettings() |
152: | { |
153: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous); |
154: | |
155: | $aSettings = [ |
156: | 'EnableModule' => false, |
157: | 'RememberPassphrase' => false |
158: | ]; |
159: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
160: | if ($oUser && $oUser->isNormalOrTenant()) { |
161: | if (isset($oUser->{self::GetName().'::EnableModule'})) { |
162: | $aSettings['EnableModule'] = $oUser->{self::GetName().'::EnableModule'}; |
163: | } |
164: | if (isset($oUser->{self::GetName().'::RememberPassphrase'})) { |
165: | $aSettings['RememberPassphrase'] = $oUser->{self::GetName().'::RememberPassphrase'}; |
166: | } |
167: | } |
168: | return $aSettings; |
169: | } |
170: | |
171: | public function UpdateSettings($EnableModule, $RememberPassphrase) |
172: | { |
173: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
174: | |
175: | $oUser = \Aurora\System\Api::getAuthenticatedUser(); |
176: | if ($oUser) { |
177: | if ($oUser->isNormalOrTenant()) { |
178: | $oCoreDecorator = \Aurora\Modules\Core\Module::Decorator(); |
179: | $oUser->setExtendedProp(self::GetName().'::EnableModule', $EnableModule); |
180: | if (isset($RememberPassphrase)) { |
181: | $oUser->setExtendedProp(self::GetName().'::RememberPassphrase', $RememberPassphrase); |
182: | } |
183: | return $oCoreDecorator->UpdateUserObject($oUser); |
184: | } |
185: | if ($oUser->Role === \Aurora\System\Enums\UserRole::SuperAdmin) { |
186: | return true; |
187: | } |
188: | } |
189: | |
190: | return false; |
191: | } |
192: | |
193: | public function AddPublicKeyToContactWithUUID($UserId, $UUID, $Key) |
194: | { |
195: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
196: | |
197: | $contact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($UUID, $UserId); |
198: | if ($contact instanceof Contact) { |
199: | $isPersonalContact = $contact->Storage === \Aurora\Modules\Contacts\Enums\StorageType::Personal; |
200: | $isTeamContact = $contact->Storage === \Aurora\Modules\Contacts\Enums\StorageType::Team; |
201: | $isItOwnContact = isset($contact->ExtendedInformation['ItsMe']) && $contact->ExtendedInformation['ItsMe'] === true; |
202: | $isReadOnly = isset($contact->ExtendedInformation['ReadOnly']) && $contact->ExtendedInformation['ReadOnly'] === true; |
203: | if ($isPersonalContact || $isTeamContact && ($isItOwnContact || !$isReadOnly)) { |
204: | $contact->setExtendedProp($this->GetName() . '::PgpKey', $Key); |
205: | return \Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($contact); |
206: | } else { |
207: | throw new \Aurora\System\Exceptions\ApiException(\Aurora\System\Notifications::AccessDenied); |
208: | } |
209: | } |
210: | |
211: | return false; |
212: | } |
213: | |
214: | public function AddPublicKeyToContact($UserId, $Email, $Key, $UserName = '') |
215: | { |
216: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
217: | |
218: | $bResult = false; |
219: | $aUpdatedContactIds = []; |
220: | if (\MailSo\Base\Validator::SimpleEmailString($Email)) { |
221: | $aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails( |
222: | $UserId, |
223: | \Aurora\Modules\Contacts\Enums\StorageType::Personal, |
224: | [$Email], |
225: | null, |
226: | false |
227: | ); |
228: | if (count($aContacts) === 0) { |
229: | $mResult = \Aurora\Modules\Contacts\Module::Decorator()->CreateContact( |
230: | [ |
231: | 'PersonalEmail' => $Email, |
232: | 'FullName' => $UserName |
233: | ], |
234: | $UserId |
235: | ); |
236: | if (isset($mResult['UUID'])) { |
237: | $oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($mResult['UUID'], $UserId); |
238: | if ($oContact instanceof Contact && |
239: | $oContact->Storage === \Aurora\Modules\Contacts\Enums\StorageType::Personal) { |
240: | $aContacts = [$oContact]; |
241: | } |
242: | } |
243: | } |
244: | |
245: | if ($aContacts && count($aContacts) > 0) { |
246: | foreach ($aContacts as $oContact) { |
247: | if ($oContact instanceof Contact && |
248: | $oContact->Storage === \Aurora\Modules\Contacts\Enums\StorageType::Personal) { |
249: | $oContact->setExtendedProp($this->GetName() . '::PgpKey', $Key); |
250: | \Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($oContact); |
251: | $aUpdatedContactIds[] = $oContact->UUID; |
252: | } |
253: | } |
254: | |
255: | |
256: | } |
257: | } |
258: | |
259: | return $aUpdatedContactIds; |
260: | } |
261: | |
262: | public function AddPublicKeysToContacts($UserId, $Keys) |
263: | { |
264: | $mResult = false; |
265: | $aUpdatedContactIds = []; |
266: | |
267: | foreach ($Keys as $aKey) { |
268: | if (isset($aKey['Email'], $aKey['Key'])) { |
269: | $sUserName = isset($aKey['Name']) ? $aKey['Name'] : ''; |
270: | $mResult = $this->AddPublicKeyToContact($UserId, $aKey['Email'], $aKey['Key'], $sUserName); |
271: | if (is_array($mResult)) { |
272: | $aUpdatedContactIds = array_merge($aUpdatedContactIds, $mResult); |
273: | } |
274: | } |
275: | } |
276: | |
277: | return $aUpdatedContactIds; |
278: | } |
279: | |
280: | public function RemovePublicKeyFromContact($UserId, $Email) |
281: | { |
282: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
283: | |
284: | $bResult = false; |
285: | |
286: | if (\MailSo\Base\Validator::SimpleEmailString($Email)) { |
287: | $aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails( |
288: | $UserId, |
289: | \Aurora\Modules\Contacts\Enums\StorageType::Personal, |
290: | [$Email], |
291: | null, |
292: | false |
293: | ); |
294: | if ($aContacts && count($aContacts) > 0) { |
295: | foreach ($aContacts as $oContact) { |
296: | if ($oContact instanceof Contact && |
297: | $oContact->Storage === \Aurora\Modules\Contacts\Enums\StorageType::Personal) { |
298: | $oContact->setExtendedProp($this->GetName() . '::PgpKey', null); |
299: | \Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($oContact); |
300: | } |
301: | } |
302: | |
303: | $bResult = true; |
304: | } |
305: | } |
306: | |
307: | return $bResult; |
308: | } |
309: | |
310: | public function GetPublicKeysByCountactUUIDs($UserId, $ContactUUIDs) |
311: | { |
312: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
313: | |
314: | $aResult = []; |
315: | |
316: | if (count($ContactUUIDs)) { |
317: | $aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByUids($UserId, $ContactUUIDs); |
318: | if (is_array($aContacts) && count($aContacts) > 0) { |
319: | foreach ($aContacts as $oContact) { |
320: | $aResult[] = [ |
321: | 'UUID' => $oContact->UUID, |
322: | 'Email' => $oContact->ViewEmail, |
323: | 'PublicPgpKey' => $oContact->{$this->GetName() . '::PgpKey'} |
324: | ]; |
325: | } |
326: | } |
327: | } |
328: | |
329: | return $aResult; |
330: | } |
331: | |
332: | protected function getContactPgpData($oContact, $iUserId) |
333: | { |
334: | if ($oContact->Storage !== StorageType::Team) { |
335: | return [ |
336: | 'PgpEncryptMessages' => (bool) $oContact->getExtendedProp($this->GetName() . '::PgpEncryptMessages', false), |
337: | 'PgpSignMessages' => (bool) $oContact->getExtendedProp($this->GetName() . '::PgpSignMessages', false) |
338: | ]; |
339: | } else { |
340: | return [ |
341: | 'PgpEncryptMessages' => (bool) $oContact->getExtendedProp($this->GetName() . '::PgpEncryptMessages_' . $iUserId, false), |
342: | 'PgpSignMessages' => (bool) $oContact->getExtendedProp($this->GetName() . '::PgpSignMessages_' . $iUserId, false) |
343: | ]; |
344: | } |
345: | } |
346: | |
347: | public function GetContactsWithPublicKeys($UserId, $UUIDs) |
348: | { |
349: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
350: | $mResult = []; |
351: | |
352: | $oContacts = Contact::whereIn('UUID', $UUIDs)->whereNotNull('Properties->' . $this->GetName() . '::PgpKey')->get(); |
353: | |
354: | if (isset($oContacts)) { |
355: | foreach ($oContacts as $oContact) { |
356: | $mResult[$oContact->UUID] = $this->getContactPgpData($oContact, $UserId); |
357: | } |
358: | } |
359: | |
360: | return $mResult; |
361: | } |
362: | |
363: | public function GetPublicKeysFromContacts($UserId) |
364: | { |
365: | \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
366: | |
367: | $aResult = []; |
368: | |
369: | $aContactsInfo = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsInfo( |
370: | \Aurora\Modules\Contacts\Enums\StorageType::Personal, |
371: | $UserId, |
372: | Contact::whereNotNull('Properties->' . $this->GetName() . '::PgpKey') |
373: | ); |
374: | |
375: | $aContactUUIDs = []; |
376: | if (is_array($aContactsInfo['Info']) && count($aContactsInfo['Info']) > 0) { |
377: | $aContactUUIDs = array_map(function ($aValue) { |
378: | return $aValue['UUID']; |
379: | }, $aContactsInfo['Info']); |
380: | } |
381: | $aResult = $this->Decorator()->GetPublicKeysByCountactUUIDs($UserId, $aContactUUIDs); |
382: | |
383: | |
384: | return $aResult; |
385: | } |
386: | |
387: | protected function updatePublicKeyFlags($UserId, $oContact, $PgpEncryptMessages = false, $PgpSignMessages = false) |
388: | { |
389: | $mResult = false; |
390: | |
391: | if ($oContact instanceof Contact) { |
392: | if ($oContact->Storage === StorageType::Team) { |
393: | $oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages_' . $UserId, $PgpEncryptMessages); |
394: | $oContact->setExtendedProp($this->GetName() . '::PgpSignMessages_' . $UserId, $PgpSignMessages); |
395: | $mResult = $oContact->save(); |
396: | } else { |
397: | $oContact->setExtendedProp($this->GetName() . '::PgpEncryptMessages', $PgpEncryptMessages); |
398: | $oContact->setExtendedProp($this->GetName() . '::PgpSignMessages', $PgpSignMessages); |
399: | $mResult = \Aurora\Modules\Contacts\Module::Decorator()->UpdateContactObject($oContact); |
400: | } |
401: | } |
402: | return $mResult; |
403: | } |
404: | |
405: | public function UpdateContactPublicKeyFlags($UserId, $UUID, $PgpEncryptMessages = false, $PgpSignMessages = false) |
406: | { |
407: | $oContact = \Aurora\Modules\Contacts\Module::Decorator()->GetContact($UUID, $UserId); |
408: | $mResult = $this->updatePublicKeyFlags($UserId, $oContact, $PgpEncryptMessages, $PgpSignMessages); |
409: | |
410: | return $mResult; |
411: | } |
412: | |
413: | protected function getTeamContactByUser($oUser) |
414: | { |
415: | $mResult = false; |
416: | |
417: | if (Api::GetModuleManager()->IsAllowedModule('TeamContacts')) { |
418: | $aContacts = \Aurora\Modules\Contacts\Module::Decorator()->GetContactsByEmails( |
419: | $oUser->Id, |
420: | \Aurora\Modules\Contacts\Enums\StorageType::Team, |
421: | [$oUser->PublicId], |
422: | null, |
423: | false |
424: | ); |
425: | if ($aContacts && count($aContacts) > 0) { |
426: | $oContact = $aContacts[0]; |
427: | if ($oContact instanceof Contact) { |
428: | $mResult = $oContact; |
429: | } |
430: | } |
431: | } |
432: | |
433: | return $mResult; |
434: | } |
435: | |
436: | public function UpdateOwnContactPublicKey($UserId, $PublicPgpKey = '') |
437: | { |
438: | $mResult = false; |
439: | |
440: | Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
441: | $oUser = Api::getAuthenticatedUser(); |
442: | if ($oUser) { |
443: | if ($oUser->Id === $UserId) { |
444: | $oContact = $this->getTeamContactByUser($oUser); |
445: | if ($oContact instanceof Contact) { |
446: | if (!empty($PublicPgpKey)) { |
447: | $oContact->setExtendedProp($this->GetName() . '::PgpKey', $PublicPgpKey); |
448: | } else { |
449: | $oContact->unsetExtendedProp($this->GetName() . '::PgpKey'); |
450: | } |
451: | $mResult = $oContact->save(); |
452: | } |
453: | } |
454: | } |
455: | |
456: | return $mResult; |
457: | } |
458: | |
459: | public function GetOwnContactPublicKey($UserId) |
460: | { |
461: | $mResult = false; |
462: | |
463: | Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser); |
464: | |
465: | $oUser = Api::getAuthenticatedUser(); |
466: | if ($oUser) { |
467: | if ($oUser->Id === $UserId) { |
468: | $oContact = $this->getTeamContactByUser($oUser); |
469: | if ($oContact instanceof Contact) { |
470: | $mResult = $oContact->getExtendedProp($this->GetName() . '::PgpKey', false); |
471: | } |
472: | } |
473: | } |
474: | |
475: | return $mResult; |
476: | } |
477: | |
478: | |
479: | |
480: | |
481: | } |
482: | |