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\MailZipWebclientPlugin;
9:
10: /**
11: * Adds Expand button on ZIP-attachment and shows its content.
12: *
13: * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
14: * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
15: * @copyright Copyright (c) 2023, Afterlogic Corp.
16: *
17: * @package Modules
18: */
19: class Module extends \Aurora\System\Module\AbstractModule
20: {
21: /*
22: * @var $oApiFileCache \Aurora\System\Managers\Filecache
23: */
24: public $oApiFileCache = null;
25:
26: public function init()
27: {
28: $this->oApiFileCache = new \Aurora\System\Managers\Filecache();
29: }
30:
31: /**
32: * Obtains list of module settings for authenticated user.
33: *
34: * @return array
35: */
36: public function GetSettings()
37: {
38: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::Anonymous);
39:
40: return array(
41: 'AllowZip' => class_exists('ZipArchive')
42: );
43: }
44:
45: public function ExpandFile($UserId, $Hash)
46: {
47: $mResult = array();
48:
49: $sUUID = \Aurora\System\Api::getUserUUIDById($UserId);
50: $aValues = \Aurora\System\Api::DecodeKeyValues($Hash);
51: $oCoreDecorator = \Aurora\Modules\Mail\Module::Decorator();
52:
53: if (isset($aValues['AccountID'])) {
54: $aFiles = $oCoreDecorator->SaveAttachmentsAsTempFiles($aValues['AccountID'], [$Hash]);
55: foreach ($aFiles as $sTempName => $sHash) {
56: if ($sHash === $Hash) {
57: $sTempZipPath = $this->oApiFileCache->generateFullFilePath($sUUID, $sTempName);
58: $mResult = $this->expandZipAttachment($sUUID, $sTempZipPath);
59: }
60: }
61: } else {
62: $sTempName = (isset($aValues['TempName'])) ? $aValues['TempName'] : 0;
63: $sTempZipPath = $this->oApiFileCache->generateFullFilePath($sUUID, $sTempName);
64: $mResult = $this->expandZipAttachment($sUUID, $sTempZipPath);
65: }
66:
67: return $mResult;
68: }
69:
70: private function expandZipAttachment($sUUID, $sTempZipPath)
71: {
72: $aResult = array();
73: $bHasMore = false;
74:
75: $oZip = new \ZipArchive();
76:
77: if (file_exists($sTempZipPath) && $oZip->open($sTempZipPath)) {
78: // Distributes files by levels.
79: $aFilesData = [];
80: for ($iIndex = 0; $iIndex < $oZip->numFiles; $iIndex++) {
81: $aStat = $oZip->statIndex($iIndex);
82: if (!empty($aStat['name'])) {
83: $aNameParts = explode('/', $aStat['name']);
84: $iFileLevel = count($aNameParts);
85: $sFileName = \MailSo\Base\Utils::Utf8Clear(basename($aStat['name']));
86: if (!isset($aFilesData[$iFileLevel]) || !is_array($aFilesData[$iFileLevel])) {
87: $aFilesData[$iFileLevel] = [];
88: }
89: $aFilesData[$iFileLevel][] = [
90: 'FileName' => $sFileName,
91: 'Index' => $iIndex
92: ];
93: }
94: }
95: // Here $aFilesData contains all levels of folders in ZIP archive.
96:
97:
98: // Reads files level by level and writes them in response until ExpandZipFilesLimit is reached.
99: $iFoldersCount = 0;
100: $iExpandZipFilesLimit = $this->getConfig('ExpandZipFilesLimit', 500);
101: foreach ($aFilesData as $aFiles) {
102: if (count($aResult) >= $iExpandZipFilesLimit) {
103: break;
104: }
105: $iFilesCount = count($aFiles);
106: for ($iFileIndex = 0; $iFileIndex < $iFilesCount && count($aResult) < $iExpandZipFilesLimit; $iFileIndex++) {
107: $aFileItemData = $aFiles[$iFileIndex];
108: $sFile = $oZip->getFromIndex($aFileItemData['Index']);
109: $iFileSize = $sFile ? strlen($sFile) : 0;
110: if ($sFile) {
111: $sTempName = md5(microtime(true).rand(1000, 9999));
112:
113: if ($this->oApiFileCache->put($sUUID, $sTempName, $sFile, '', self::GetName())) {
114: unset($sFile);
115:
116: $aResult[] = \Aurora\System\Utils::GetClientFileResponse(
117: self::GetName(),
118: \Aurora\System\Api::getAuthenticatedUserId(),
119: $aFileItemData['FileName'],
120: $sTempName,
121: $iFileSize
122: );
123: } else {
124: unset($sFile);
125: }
126: } else {
127: // Counts all items that shouldn't be in response (they are folders usually).
128: $iFoldersCount++;
129: }
130: }
131: }
132: // Determines if there are more files not in the response (because of ExpandZipFilesLimit).
133: $bHasMore = ($iFoldersCount + count($aResult)) < $oZip->numFiles;
134: $oZip->close();
135: }
136: return [
137: 'Files' => $aResult,
138: 'HasMore' => $bHasMore
139: ];
140: }
141:
142: /**
143: * @param int $UserId
144: * @param int $AccountID
145: * @param array $Attachments
146: * @return boolean
147: */
148: public function SaveAttachments($UserId, $AccountID, $Attachments = array())
149: {
150: $mResult = false;
151: \Aurora\System\Api::checkUserRoleIsAtLeast(\Aurora\System\Enums\UserRole::NormalUser);
152:
153: $aAddFiles = array();
154:
155: $oMailModuleDecorator = \Aurora\Modules\Mail\Module::Decorator();
156: if ($oMailModuleDecorator) {
157: $aTempFiles = $oMailModuleDecorator->SaveAttachmentsAsTempFiles($AccountID, $Attachments);
158: if (\is_array($aTempFiles)) {
159: $sUUID = \Aurora\System\Api::getUserUUIDById($UserId);
160: foreach ($aTempFiles as $sTempName => $sData) {
161: $aData = \Aurora\System\Api::DecodeKeyValues($sData);
162: if (\is_array($aData) && isset($aData['FileName'])) {
163: $sFileName = (string) $aData['FileName'];
164: $sTempPath = $this->oApiFileCache->generateFullFilePath($sUUID, $sTempName);
165: $aAddFiles[] = array($sTempPath, $sFileName);
166: }
167: }
168: }
169: }
170:
171: if (count($aAddFiles) > 0) {
172: $oZip = new \ZipArchive();
173:
174: $sZipTempName = md5(microtime());
175: $sZipTempPath = $this->oApiFileCache->generateFullFilePath($sUUID, $sZipTempName, '', self::GetName());
176: if ($oZip->open($sZipTempPath, \ZipArchive::CREATE)) {
177: foreach ($aAddFiles as $aItem) {
178: $oZip->addFile($aItem[0], $aItem[1]);
179: }
180: $oZip->close();
181: $iFileSize = $this->oApiFileCache->fileSize($sUUID, $sZipTempName, '', self::GetName());
182: $mResult = \Aurora\System\Utils::GetClientFileResponse(
183: self::GetName(),
184: $UserId,
185: 'attachments.zip',
186: $sZipTempName,
187: $iFileSize
188: );
189: }
190: }
191:
192: return $mResult;
193: }
194: }
195: