Test Failed
Push — master ( 647c72...cd42b5 )
by
unknown
10:25
created

DownloadBase::getMessageType()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 29
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 23
nc 7
nop 1
dl 0
loc 29
rs 8.6186
c 0
b 0
f 0
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));
0 ignored issues
show
Bug introduced by
$this->storeId of type true is incompatible with the type string expected by parameter $string of hex2bin(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

73
			$this->store = $GLOBALS['mapisession']->openMessageStore(hex2bin(/** @scrutinizer ignore-type */ $this->storeId));
Loading history...
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
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

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.

Loading history...
297