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

RulesModule::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
	/**
3
	 * Rules Module
4
	 * Module will be used to save rules information to rules table
5
	 */
6
	class RulesModule extends Module
7
	{
8
		/**
9
		 * @var MAPITable contains resource of rules modify table.
0 ignored issues
show
Bug introduced by
The type MAPITable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
		 */
11
		private $rulesFolder;
12
13
		/**
14
		 * Constructor
15
		 * @param int $id unique id.
16
		 * @param array $data list of all actions.
17
		 */
18
		function __construct($id, $data)
19
		{
20
			$this->properties = $GLOBALS['properties']->getRulesProperties();
0 ignored issues
show
Bug Best Practice introduced by
The property properties does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
21
			$this->rulesFolder = null;
22
23
			parent::__construct($id, $data);
24
		}
25
26
		/**
27
		 * Executes all the actions in the $data variable.
28
		 * @return boolean true on success of false on failure.
29
		 */
30
		function execute()
31
		{
32
			foreach($this->data as $actionType => $action)
33
			{
34
35
				// Determine if the request contains multiple items or not. We couldn't add the storeEntryId to
36
				// the action data if it contained items because it was an array, so the storeEntryId
37
				// was added to all the items. We will pick it from the first item.
38
				if (isset($action[0])) {
39
					$storeEntryid = $action[0]['message_action']['store_entryid'];
40
				} else {
41
					$storeEntryid = $action['store_entryid'];
42
				}
43
44
				$ownStoreEntryId = $GLOBALS['mapisession']->getDefaultMessageStoreEntryId();
45
46
				try {
47
					if ( ENABLE_SHARED_RULES !== true && !$GLOBALS['entryid']->compareStoreEntryIds($storeEntryid, $ownStoreEntryId) ){
48
						// When the admin does not allow a user to set rules on the store of other users, but somehow
49
						// the user still tries this (probably hacking) we will not allow this
50
						throw new MAPIException(_('Setting mail filters on the stores of other users is not allowed.'));
51
					} else {
52
						$store = $GLOBALS['mapisession']->openMessageStore(hex2bin($storeEntryid));
53
					}
54
55
					switch($actionType) {
56
						case 'list':
57
							$rules = $this->getRules($store);
58
							if ($rules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rules of type array<string,array|array...d,array<string,array>>> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
59
								$this->addActionData('list', $rules);
60
								$GLOBALS['bus']->addData($this->getResponseData());
61
							} else {
62
								$this->sendFeedback(false);
63
							}
64
							break;
65
						case 'save':
66
							// When saving the rules, we expect _all_ rules
67
							// to have been send. So our first task, is to
68
							// delete all existing rules.
69
							$this->deleteRules($store);
70
71
							// Now can save all rules, note that $action can contain just a store key when
72
							// all rules are removed.
73
							if (count($action) > 1) {
74
								$this->saveRules($store, $action);
75
							}
76
77
							// delete (outlook) client rules
78
							$this->deleteOLClientRules($store);
79
80
							// Respond with the full set of rules.
81
							$rules = $this->getRules($store);
82
							if ($rules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $rules of type array<string,array|array...d,array<string,array>>> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
83
								$this->addActionData('update', $rules);
84
								$GLOBALS['bus']->addData($this->getResponseData());
85
							} else {
86
								$this->sendFeedback(false);
87
							}
88
							break;
89
						default:
90
							$this->handleUnknownActionType($actionType);
91
					}
92
				} catch (MAPIException $e) {
93
					$this->processException($e, $actionType);
94
				}
95
			}
96
		}
97
98
		function getRulesFolder($store = false)
99
		{
100
			if(!$this->rulesFolder) {
101
				if($store === false) {
102
					$store = $GLOBALS['mapisession']->getDefaultMessageStore();
103
				}
104
				$this->rulesFolder = mapi_msgstore_getreceivefolder($store);
105
			}
106
107
			return $this->rulesFolder;
108
		}
109
110
		/**
111
		 * Create a restriction to search for rules which have a rule provider
112
		 * which starts with RuleOrganizer. Outlook will generate some rules
113
		 * with PR_RULE_PROVIDER RuleOrganizer2 for client-only rules, however
114
		 * we still want to show these in the client, hence we perform a prefix
115
		 * search.
116
		 * @return {Array} The restriction which should be applied to the RulesTable
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Array} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Array}.
Loading history...
117
		 * to obtain all the rules which should be shown to the user
118
		 */
119
		function getRestriction()
120
		{
121
			return array(RES_CONTENT,
122
				array(
123
					FUZZYLEVEL      =>      FL_PREFIX | FL_IGNORECASE,
124
					ULPROPTAG       =>      PR_RULE_PROVIDER,
125
					VALUE           =>      array(
126
						PR_RULE_PROVIDER        =>      'RuleOrganizer'
127
					)
128
				)
129
			);
130
		}
131
132
		/**
133
		 * Get all rules of a store
134
		 *
135
		 * This function opens the rules table for the specified store, and reads
136
		 * all rules with PR_RULE_PROVIDER equal to 'RuleOrganizer'. These are the rules
137
		 * that the user sees when managing rules from Outlook.
138
		 *
139
		 * @param {MAPIStore} $store Store in which rules reside
0 ignored issues
show
Documentation Bug introduced by
The doc comment {MAPIStore} at position 0 could not be parsed: Unknown type name '{' at position 0 in {MAPIStore}.
Loading history...
140
		 * @return {Array} rules data
0 ignored issues
show
Documentation Bug introduced by
The doc comment {Array} at position 0 could not be parsed: Unknown type name '{' at position 0 in {Array}.
Loading history...
141
		 */
142
		function getRules($store)
143
		{
144
			$rules_folder = $this->getRulesFolder($store);
145
			$rulesTable = mapi_folder_getrulestable($rules_folder);
146
147
			mapi_table_restrict($rulesTable, $this->getRestriction(), TBL_BATCH);
148
			mapi_table_sort($rulesTable, array(PR_RULE_SEQUENCE => TABLE_SORT_ASCEND), TBL_BATCH);
149
150
			$rows = mapi_table_queryallrows($rulesTable, $this->properties);
151
152
			$rules = array();
153
154
			foreach ($rows as &$row) {
155
				$rules[] = Conversion::mapMAPI2XML($this->properties, $row);
156
			}
157
			unset($row);
158
159
			return Array('item' => $rules);
160
		}
161
162
		/**
163
		 * Function used to delete all the rules the user currently has.
164
		 * @param {MAPIStore} $store in which we want to delete the rules.
0 ignored issues
show
Documentation Bug introduced by
The doc comment {MAPIStore} at position 0 could not be parsed: Unknown type name '{' at position 0 in {MAPIStore}.
Loading history...
165
		 */
166
		function deleteRules($store)
167
		{
168
			$rules_folder = $this->getRulesFolder($store);
169
			$rulesTable = mapi_folder_getrulestable($rules_folder);
170
			mapi_table_restrict($rulesTable, $this->getRestriction(), TBL_BATCH);
171
			mapi_table_sort($rulesTable, array(PR_RULE_SEQUENCE => TABLE_SORT_ASCEND), TBL_BATCH);
172
			$rows = mapi_table_queryallrows($rulesTable, $this->properties);
173
174
			$rules = array();
175
176
			foreach ($rows as &$row) {
177
				$rules[] = array(
178
					'rowflags' => ROW_REMOVE,
179
					'properties' => $row
180
				);
181
			}
182
183
			if (!empty($rules)) {
184
				mapi_folder_modifyrules($rules_folder, $rules);
185
			}
186
		}
187
188
		/**
189
		 * Function will be used to create/update rule in user's rules table.
190
		 * This function only usee ROW_MODIFY flag to save rules data, Which is correct when modifying existing rules
191
		 * but for adding new rules Gromox automatically checks existence of rule id and if it si not then
192
		 * use ROW_ADD flag.
193
		 * @param {MAPIStore} $store The store into which the rules must be saved.
0 ignored issues
show
Documentation Bug introduced by
The doc comment {MAPIStore} at position 0 could not be parsed: Unknown type name '{' at position 0 in {MAPIStore}.
Loading history...
194
		 * @param {Array} $rulesData rules data that should be deleted.
195
		 */
196
		function saveRules($store, $rulesData)
197
		{
198
			if (is_assoc_array($rulesData)) {
199
				// wrap single rule in an array
200
				$rulesData = array($rulesData);
201
			}
202
203
			// save rules in rules table
204
			$saveRules = array();
205
			for ($index = 0, $len = count($rulesData); $index < $len; $index++) {
206
				$rule = $rulesData[$index];
207
				if (!empty($rule['props'])) {
208
					$rule += $rule['props'];
209
				}
210
211
				$rule = Conversion::mapXML2MAPI($this->properties, $rule);
212
213
				// Always reset the PR_RULE_ID property, it is going
214
				// to be regenerated by the server anyway, so we can safely
215
				// discard whatever value the client has given.
216
				$rule[PR_RULE_ID] = $index;
217
218
				// provide default action and rule if client has not provided
219
				if (empty($rule[PR_RULE_ACTIONS])) {
220
					$rule[PR_RULE_ACTIONS] = array(
221
						array(
222
							'action' => OP_DEFER_ACTION,
223
							'dam' => hex2bin('E0C810000120000100000000000000010000000000000001000000360000000200FFFF00000C004352756C65456C656D656E7490010000010000000000000001000000018064000000010000000000000001000000')
224
						)
225
					);
226
				}
227
228
				if (empty($rule[PR_RULE_CONDITION])) {
229
					$rule[PR_RULE_CONDITION] = array(
230
						RES_EXIST,
231
						array(
232
							ULPROPTAG => PR_MESSAGE_CLASS
233
						)
234
					);
235
				}
236
237
				if (empty($rule[PR_RULE_NAME])) {
238
					$rule[PR_RULE_NAME] = _('Untitled rule');
239
				}
240
241
				// generate rule provider data
242
				$rule[PR_RULE_PROVIDER_DATA] = pack('VVa*', 1, $rule[PR_RULE_ID], Conversion::UnixTimeToCOleDateTime(time()));
243
244
				$saveRules[] = array(
245
					'rowflags' => ROW_ADD,
246
					'properties' => $rule
247
				);
248
			}
249
250
			if (!empty($saveRules)) {
251
				mapi_folder_modifyrules($this->getRulesFolder($store), $saveRules);
252
			}
253
		}
254
255
		/**
256
		 * Function will delete (outlook) client rules. Outlook maintains client rules
257
		 * in associated table of inbox, When we create/delete/update rule from webapp
258
		 * it won't match with outlook's client rules, so it will confuse outlook and
259
		 * it will ask user to preserve whether client or server side rules, so every time
260
		 * we save rules we need to remove this outlook generated client rule to remove
261
		 * ambigiuty.
262
		 *
263
		 * @param {MAPIStore} $store (optional) current user's store.
0 ignored issues
show
Documentation Bug introduced by
The doc comment {MAPIStore} at position 0 could not be parsed: Unknown type name '{' at position 0 in {MAPIStore}.
Loading history...
264
		 */
265
		function deleteOLClientRules($store = false)
266
		{
267
			if($store === false) {
268
				$store = $GLOBALS['mapisession']->getDefaultMessageStore();
269
			}
270
271
			$inbox = mapi_msgstore_getreceivefolder($store);
272
273
			// get inbox' associatedTable
274
			$associatedTable = mapi_folder_getcontentstable($inbox, MAPI_ASSOCIATED);
275
276
			mapi_table_restrict($associatedTable,
277
						array(RES_CONTENT,
278
							array(
279
								FUZZYLEVEL	=>	FL_FULLSTRING | FL_IGNORECASE,
280
								ULPROPTAG	=>	PR_MESSAGE_CLASS,
281
								VALUE		=>	array(
282
									PR_MESSAGE_CLASS	=>	"IPM.RuleOrganizer"
283
								)
284
							)
285
						)
286
								);
287
			$messages = mapi_table_queryallrows($associatedTable, array(PR_ENTRYID));
288
289
			$deleteMessages = array();
290
			for ($i = 0, $len = count($messages); $i < $len; $i++) {
291
				array_push($deleteMessages, $messages[$i][PR_ENTRYID]);
292
			}
293
294
			if (!empty($deleteMessages)) {
295
				mapi_folder_deletemessages($inbox, $deleteMessages);
296
			}
297
		}
298
299
		/**
300
		 * Function does customization of MAPIException based on module data.
301
		 * like, here it will generate display message based on actionType
302
		 * for particular exception.
303
		 *
304
		 * @param object $e Exception object.
305
		 * @param string $actionType the action type, sent by the client.
306
		 * @param MAPIobject $store Store object of the message.
0 ignored issues
show
Bug introduced by
The type MAPIobject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
307
		 * @param string $parententryid parent entryid of the message.
308
		 * @param string $entryid entryid of the message/folder.
309
		 * @param array $action the action data, sent by the client.
310
		 */
311
		function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null)
312
		{
313
			if (is_null($e->displayMessage)) {
314
				switch ($actionType)
315
				{
316
					case 'list':
317
						$e->setDisplayMessage(_('Could not load rules.'));
318
						break;
319
					case 'save':
320
						$e->setDisplayMessage(_('Could not save rules.'));
321
						break;
322
				}
323
			}
324
		}
325
	}
326
?>
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...
327