grommunio /
grommunio-web
| 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. |
||
| 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(); |
||
| 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) { |
||
| 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) { |
||
| 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 |
||
| 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 |
||
| 140 | * @return {Array} rules data |
||
| 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. |
||
| 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. |
||
| 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. |
||
| 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. |
||
| 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
|
|||
| 327 |
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.