grommunio /
grommunio-web
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * DownloadBase |
||
| 4 | * |
||
| 5 | * An abstract class which serve as base to manage downloading |
||
| 6 | */ |
||
| 7 | abstract class DownloadBase |
||
| 8 | { |
||
| 9 | /** |
||
| 10 | * Resource of the MAPIStore which holds the message which we need to save as file. |
||
| 11 | */ |
||
| 12 | protected $store; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * Entryid of the MAPIStore which holds the message which we need to save as file. |
||
| 16 | */ |
||
| 17 | protected $storeId; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Entryid of the MAPIMessage which we need to save as file. |
||
| 21 | */ |
||
| 22 | protected $entryId; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Array of entryids of all the MAPIMessages which we need to include in ZIP. |
||
| 26 | */ |
||
| 27 | protected $entryIds; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * Resource of MAPIMessage which we need to save as file. |
||
| 31 | */ |
||
| 32 | protected $message; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * A boolean value, set to false by default, to define if all the attachments are requested to be downloaded in a zip or not. |
||
| 36 | */ |
||
| 37 | protected $allAsZip; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Array to hold file names added into the ZIP which is used to avoid similar file names to be added in ZIP. |
||
| 41 | */ |
||
| 42 | protected $fileNameTracker; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Constructor |
||
| 46 | */ |
||
| 47 | public function __construct() |
||
| 48 | { |
||
| 49 | $this->storeId = false; |
||
| 50 | $this->entryId = false; |
||
| 51 | $this->allAsZip = false; |
||
| 52 | $this->fileNameTracker = array(); |
||
| 53 | $this->entryIds = array(); |
||
| 54 | $this->message = false; |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * Function will initialize data for this class object. it will also sanitize data |
||
| 59 | * for possible XSS attack because data is received in $_GET |
||
| 60 | * @param Array $data parameters received with the request. |
||
| 61 | */ |
||
| 62 | public function init($data) |
||
| 63 | { |
||
| 64 | if(isset($data['storeid'])) { |
||
| 65 | $this->storeId = sanitizeValue($data['storeid'], '', ID_REGEX); |
||
| 66 | } |
||
| 67 | |||
| 68 | if(isset($data['AllAsZip'])) { |
||
| 69 | $this->allAsZip = sanitizeValue($data['AllAsZip'], '', STRING_REGEX); |
||
| 70 | } |
||
| 71 | |||
| 72 | if($this->storeId){ |
||
| 73 | $this->store = $GLOBALS['mapisession']->openMessageStore(hex2bin($this->storeId)); |
||
| 74 | } |
||
| 75 | |||
| 76 | if($this->allAsZip){ |
||
| 77 | if($_POST && array_key_exists('entryids', $_POST) && isset($_POST['entryids'])) { |
||
| 78 | $this->entryIds = $_POST['entryids']; |
||
| 79 | } |
||
| 80 | } else { |
||
| 81 | if(isset($data['entryid'])) { |
||
| 82 | $this->entryId = sanitizeValue($data['entryid'], '', ID_REGEX); |
||
| 83 | $this->message = mapi_msgstore_openentry($this->store, hex2bin($this->entryId)); |
||
| 84 | |||
| 85 | // Decode smime signed messages on this message |
||
| 86 | parse_smime($this->store, $this->message); |
||
| 87 | } |
||
| 88 | } |
||
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * Offers the functionality to postfixed the file name with number derived from |
||
| 93 | * the appearance of other file with same name. |
||
| 94 | * We need to keep track of the file names used so far to prevent duplicates. |
||
| 95 | * @param String $filename name of the file to be added in ZIP. |
||
| 96 | * @return String $filename changed name of the file to be added in ZIP to avoid same file names. |
||
| 97 | */ |
||
| 98 | function handleDuplicateFileNames($filename) |
||
| 99 | { |
||
| 100 | // A local name is optional. |
||
| 101 | if(!empty($filename)) { |
||
| 102 | |||
| 103 | // Check and add if file name is not there in tracker array |
||
| 104 | if(!array_key_exists($filename, $this->fileNameTracker)) { |
||
| 105 | $this->fileNameTracker[$filename] = 0; |
||
| 106 | } |
||
| 107 | |||
| 108 | // We have to make sure that there aren't two of the same file names. |
||
| 109 | // Otherwise, one file will override the other and we will be missing the file. |
||
| 110 | while( $this->fileNameTracker[ $filename ] > 0 ) { |
||
| 111 | $fileNameInfo = pathinfo( $filename ); |
||
| 112 | $intNext = $this->fileNameTracker[ $filename ]++; |
||
| 113 | $filename = "$fileNameInfo[filename] ($intNext).$fileNameInfo[extension]"; |
||
| 114 | |||
| 115 | // Check and add if newly prepared file name is not there in tracker array |
||
| 116 | if(!array_key_exists($filename, $this->fileNameTracker)){ |
||
| 117 | $this->fileNameTracker[$filename] = 0; |
||
| 118 | } |
||
| 119 | } |
||
| 120 | |||
| 121 | // Add to the count. |
||
| 122 | $this->fileNameTracker[ $filename ]++; |
||
| 123 | } |
||
| 124 | return $filename; |
||
| 125 | } |
||
| 126 | |||
| 127 | /** |
||
| 128 | * Helper function to set necessary headers. |
||
| 129 | * @param String $filename Proper name for the file to be downloaded. |
||
| 130 | * @param Number $contentLength Total size of file content. |
||
| 131 | */ |
||
| 132 | function setNecessaryHeaders($filename, $contentLength) |
||
| 133 | { |
||
| 134 | // Set the headers |
||
| 135 | header('Pragma: public'); |
||
| 136 | header('Expires: 0'); // set expiration time |
||
| 137 | header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); |
||
| 138 | header('Content-Transfer-Encoding: binary'); |
||
| 139 | |||
| 140 | // Set Content Disposition header |
||
| 141 | header('Content-Disposition: attachment; filename="' . addslashes(browserDependingHTTPHeaderEncode($filename)) . '"'); |
||
| 142 | // Set content type header |
||
| 143 | header('Content-Type: application/octet-stream'); |
||
| 144 | |||
| 145 | // Set the file length |
||
| 146 | header('Content-Length: ' . $contentLength); |
||
| 147 | } |
||
| 148 | |||
| 149 | /** |
||
| 150 | * Function will return message type according to the MAPI message class |
||
| 151 | * to display exception. so, user can easily understand the exception message. |
||
| 152 | * |
||
| 153 | * @param string $mapiMessageClass message type as defined in MAPI. |
||
| 154 | * @return string $messageClass message type to prepare exception message. |
||
| 155 | */ |
||
| 156 | function getMessageType($mapiMessageClass) |
||
| 157 | { |
||
| 158 | $messageClass = ''; |
||
| 159 | |||
| 160 | // Convert message class into human readable format, so user can easily understand the display message. |
||
| 161 | switch ($this->getTrimmedMessageClass($mapiMessageClass)) { |
||
| 162 | case 'Appointment': |
||
| 163 | $messageClass = _('Appointment'); |
||
| 164 | break; |
||
| 165 | case 'StickyNote': |
||
| 166 | $messageClass = _('Sticky Note'); |
||
| 167 | break; |
||
| 168 | case 'Contact': |
||
| 169 | $messageClass = _('Contact'); |
||
| 170 | break; |
||
| 171 | case 'DistList': |
||
| 172 | $messageClass = _('Distribution list'); |
||
| 173 | break; |
||
| 174 | case 'Task': |
||
| 175 | $messageClass = _('Task'); |
||
| 176 | break; |
||
| 177 | case 'TaskRequest': |
||
| 178 | $messageClass = _('Task Request'); |
||
| 179 | break; |
||
| 180 | default: |
||
| 181 | $messageClass = $mapiMessageClass; |
||
| 182 | } |
||
| 183 | |||
| 184 | return $messageClass; |
||
| 185 | } |
||
| 186 | |||
| 187 | /** |
||
| 188 | * Returns message-class removing technical prefix/postfix from |
||
| 189 | * original/technical message class. |
||
| 190 | * |
||
| 191 | * @param string $mapiMessageClass message type as defined in MAPI. |
||
| 192 | * @return string $messageClass message class without any prefix/postfix. |
||
| 193 | */ |
||
| 194 | function getTrimmedMessageClass($mapiMessageClass) |
||
| 195 | { |
||
| 196 | // Here, we have technical message class, so we need to remove technical prefix/postfix, if any. |
||
| 197 | // Creates an array of strings by splitting the message class from dot(.) |
||
| 198 | $explodedMessageClass = explode(".", $mapiMessageClass); |
||
| 199 | $ipmIndex = array_search('IPM', $explodedMessageClass); |
||
| 200 | |||
| 201 | return $explodedMessageClass[$ipmIndex + 1]; |
||
| 202 | } |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Function will encode all the necessary information about the exception |
||
| 206 | * into JSON format and send the response back to client. |
||
| 207 | * |
||
| 208 | * @param object $exception Exception object. |
||
| 209 | */ |
||
| 210 | function handleSaveMessageException($exception) |
||
| 211 | { |
||
| 212 | $return = array(); |
||
| 213 | |||
| 214 | // MAPI_E_NOT_FOUND exception contains generalize exception message. |
||
| 215 | // Set proper exception message as display message should be user understandable. |
||
| 216 | if($exception->getCode() == MAPI_E_NOT_FOUND) { |
||
| 217 | $exception->setDisplayMessage(_('Could not find message, either it has been moved or deleted.')); |
||
| 218 | } |
||
| 219 | |||
| 220 | // Set the headers |
||
| 221 | header('Expires: 0'); // set expiration time |
||
| 222 | header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); |
||
| 223 | |||
| 224 | // Set Content Disposition header |
||
| 225 | header('Content-Disposition: inline'); |
||
| 226 | // Set content type header |
||
| 227 | header('Content-Type: text/plain'); |
||
| 228 | |||
| 229 | //prepare exception response according to exception class |
||
| 230 | if($exception instanceof MAPIException) { |
||
| 231 | $return = array( |
||
| 232 | 'success' => false, |
||
| 233 | 'zarafa' => array( |
||
| 234 | 'error' => array( |
||
| 235 | 'type' => ERROR_MAPI, |
||
| 236 | 'info' => array( |
||
| 237 | 'hresult' => $exception->getCode(), |
||
| 238 | 'hresult_name' => get_mapi_error_name($exception->getCode()), |
||
| 239 | 'file' => $exception->getFileLine(), |
||
| 240 | 'display_message' => $exception->getDisplayMessage() |
||
| 241 | ) |
||
| 242 | ) |
||
| 243 | ) |
||
| 244 | ); |
||
| 245 | } else if($exception instanceof ZarafaException) { |
||
| 246 | $return = array( |
||
| 247 | 'success' => false, |
||
| 248 | 'zarafa' => array( |
||
| 249 | 'error' => array( |
||
| 250 | 'type' => ERROR_ZARAFA, |
||
| 251 | 'info' => array( |
||
| 252 | 'file' => $exception->getFileLine(), |
||
| 253 | 'display_message' => $exception->getDisplayMessage(), |
||
| 254 | 'original_message' => $exception->getMessage() |
||
| 255 | ) |
||
| 256 | ) |
||
| 257 | ) |
||
| 258 | ); |
||
| 259 | } else if($exception instanceof BaseException) { |
||
| 260 | $return = array( |
||
| 261 | 'success' => false, |
||
| 262 | 'zarafa' => array( |
||
| 263 | 'error' => array( |
||
| 264 | 'type' => ERROR_GENERAL, |
||
| 265 | 'info' => array( |
||
| 266 | 'file' => $exception->getFileLine(), |
||
| 267 | 'display_message' => $exception->getDisplayMessage(), |
||
| 268 | 'original_message' => $exception->getMessage() |
||
| 269 | ) |
||
| 270 | ) |
||
| 271 | ) |
||
| 272 | ); |
||
| 273 | } else { |
||
| 274 | $return = array( |
||
| 275 | 'success' => false, |
||
| 276 | 'zarafa' => array( |
||
| 277 | 'error' => array( |
||
| 278 | 'type' => ERROR_GENERAL, |
||
| 279 | 'info' => array( |
||
| 280 | 'display_message' => _('Operation failed'), |
||
| 281 | 'original_message' => $exception->getMessage() |
||
| 282 | ) |
||
| 283 | ) |
||
| 284 | ) |
||
| 285 | ); |
||
| 286 | } |
||
| 287 | echo json_encode($return); |
||
| 288 | } |
||
| 289 | |||
| 290 | /** |
||
| 291 | * Abstract Generic function to check received data and decide either the eml/vcf file or |
||
| 292 | * ZIP file is requested to be downloaded. |
||
| 293 | */ |
||
| 294 | abstract protected function download(); |
||
| 295 | } |
||
| 296 | ?> |
||
|
0 ignored issues
–
show
|
|||
| 297 |
Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.
A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.