| 1 |  |  | <?php | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | require_once BASE_PATH . 'server/includes/core/class.indexsqlite.php'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | class AdvancedSearchListModule extends ListModule { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | 	 * Constructor. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | 	 * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | 	 * @param int   $id   unique id | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  | 	 * @param array $data list of all actions | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | 	 */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | 	public function __construct($id, $data) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | 		parent::__construct($id, $data); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | 		// TODO: create a new method in Properties class that will return only the properties we | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  | 		// need for search list (and perhaps for preview???) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | 		$this->properties = $GLOBALS["properties"]->getMailListProperties(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  | 		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getAppointmentListProperties()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  | 		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getContactListProperties()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  | 		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getStickyNoteListProperties()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  | 		$this->properties = array_merge($this->properties, $GLOBALS["properties"]->getTaskListProperties()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  | 		$this->properties = array_merge($this->properties, [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  | 			'body' => PR_BODY, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  | 			'html_body' => PR_HTML, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  | 			'startdate' => "PT_SYSTIME:PSETID_Appointment:" . PidLidAppointmentStartWhole, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  | 			'duedate' => "PT_SYSTIME:PSETID_Appointment:" . PidLidAppointmentEndWhole, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  | 			'creation_time' => PR_CREATION_TIME, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  | 			"task_duedate" => "PT_SYSTIME:PSETID_Task:" . PidLidTaskDueDate, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  | 		$this->properties = getPropIdsFromStrings($GLOBALS["mapisession"]->getDefaultMessageStore(), $this->properties); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  | 		$this->sort = [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  | 			PR_MESSAGE_DELIVERY_TIME => TABLE_SORT_DESCEND, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  | 		]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  | 	 * Executes all the actions in the $data variable. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  | 	 */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  | 	#[Override] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  | 	public function execute() { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  | 		foreach ($this->data as $actionType => $action) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  | 			if (isset($actionType)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  | 				try { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  | 					$store = $this->getActionStore($action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  | 					$parententryid = $this->getActionParentEntryID($action); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  | 					$entryid = $this->getActionEntryID($action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  | 					switch ($actionType) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  | 						case "list": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  | 						case "updatelist": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  | 							$this->getDelegateFolderInfo($store); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  | 							$this->messageList($store, $entryid, $action, $actionType); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  | 							break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  | 						case "search": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  | 							$this->search($store, $entryid, $action, $actionType); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  | 							break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  | 						case "updatesearch": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  | 							$this->updatesearch($store, $entryid, $action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  | 							break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  | 						case "stopsearch": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  | 							$this->stopSearch($store, $entryid, $action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  | 							break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  | 						case "delete_searchfolder": | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  | 							$this->deleteSearchFolder($store, $entryid, $action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  | 							break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  | 				catch (MAPIException $e) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  | 					// This is a very nasty hack that makes sure that grommunio Web doesn't show an error message when | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  | 					// search wants to throw an error. This is only done because a proper fix for this bug has not | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  | 					// been found yet. When WA-9161 is really solved, this should be removed again. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  | 					if ($actionType !== 'search' && $actionType !== 'updatesearch' && $actionType !== 'stopsearch') { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 |  |  | 						$this->processException($e, $actionType); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  | 					else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 |  |  | 						if (DEBUG_LOADER === 0) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  | 							// Log all info we can get about this error to the error log of the web server | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  | 							error_log("Error in search: \n" . var_export($e, true) . "\n\n" . var_export(debug_backtrace(), true)); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  | 						} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  | 						// Send success feedback without data, as if nothing strange happened... | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  | 						$this->sendFeedback(true); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  | 	 * Function which retrieves a list of messages in a folder. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  | 	 * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  | 	 * @param object $store      MAPI Message Store Object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  | 	 * @param string $entryid    entryid of the folder | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  | 	 * @param array  $action     the action data, sent by the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  | 	 * @param string $actionType the action type, sent by the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  | 	 */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  | 	#[Override] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  | 	public function messageList($store, $entryid, $action, $actionType) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  | 		$this->searchFolderList = false; // Set to indicate this is not the search result, but a normal folder content | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  | 		$data = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  | 		if ($store && $entryid) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  | 			// Restriction | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  | 			$this->parseRestriction($action); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  | 			// Sort | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  | 			$this->parseSortOrder($action, null, true); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  | 			$limit = $action['restriction']['limit'] ?? 1000; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  | 			$isSearchFolder = isset($action['search_folder_entryid']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  | 			$entryid = $isSearchFolder ? hex2bin((string) $action['search_folder_entryid']) : $entryid; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  | 			if ($actionType == 'search') { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  | 				$rows = [[PR_ENTRYID => $entryid]]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  | 				if (isset($action['subfolders']) && $action['subfolders']) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  | 					$folder = mapi_msgstore_openentry($store, $entryid); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  | 					$htable = mapi_folder_gethierarchytable($folder, CONVENIENT_DEPTH | MAPI_DEFERRED_ERRORS); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  | 					$rows = mapi_table_queryallrows($htable, [PR_ENTRYID]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  | 				$data['item'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  | 				foreach ($rows as $row) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  | 					$items = $GLOBALS["operations"]->getTable($store, $row[PR_ENTRYID], $this->properties, $this->sort, $this->start, $limit, $this->restriction); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  | 					$data['item'] = array_merge($data['item'], $items['item']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  | 					if (count($data['item']) >= $limit) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  | 						break; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  | 				$data['page'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  | 				$data['page']['start'] = 0; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  | 				$data['page']['rowcount'] = 0; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  | 				$data['page']['totalrowcount'] = count($data['item']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  | 				$data['search_meta'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  | 				$data['search_meta']['searchfolder_entryid'] = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  | 				$data['search_meta']['search_store_entryid'] = $action['store_entryid']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  | 				$data['search_meta']['searchstate'] = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  | 				$data['search_meta']['results'] = count($data['item']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  | 				$data['folder'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  | 				$data['folder']['content_count'] = count($data['item']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  | 				$data['folder']['content_unread'] = 0; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  | 			else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  | 				// Get the table and merge the arrays | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  | 				$data = $GLOBALS["operations"]->getTable($store, $entryid, $this->properties, $this->sort, $this->start, $limit, $this->restriction); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  | 			// If the request come from search folder then no need to send folder information | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  | 			if (!$isSearchFolder && !isset($data['folder'])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  | 				// Open the folder. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  | 				$folder = mapi_msgstore_openentry($store, $entryid); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  | 				$data["folder"] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  | 				// Obtain some statistics from the folder contents | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  | 				$contentcount = mapi_getprops($folder, [PR_CONTENT_COUNT, PR_CONTENT_UNREAD]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  | 				if (isset($contentcount[PR_CONTENT_COUNT])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  | 					$data["folder"]["content_count"] = $contentcount[PR_CONTENT_COUNT]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  | 				if (isset($contentcount[PR_CONTENT_UNREAD])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  | 					$data["folder"]["content_unread"] = $contentcount[PR_CONTENT_UNREAD]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 163 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 164 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 165 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 166 |  |  | 			$data = $this->filterPrivateItems($data); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 167 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 168 |  |  | 			// Allowing to hook in just before the data sent away to be sent to the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 169 |  |  | 			$GLOBALS['PluginManager']->triggerHook('server.module.listmodule.list.after', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 170 |  |  | 				'moduleObject' => &$this, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 171 |  |  | 				'store' => $store, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 172 |  |  | 				'entryid' => $entryid, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 173 |  |  | 				'action' => $action, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 174 |  |  | 				'data' => &$data, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 175 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 176 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 177 |  |  | 			// unset will remove the value but will not regenerate array keys, so we need to | 
            
                                                                                                            
                            
            
                                    
            
            
                | 178 |  |  | 			// do it here | 
            
                                                                                                            
                            
            
                                    
            
            
                | 179 |  |  | 			$data["item"] = array_values($data["item"]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 180 |  |  | 			$this->addActionData($actionType, $data); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 181 |  |  | 			$GLOBALS["bus"]->addData($this->getResponseData()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 182 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 183 |  |  | 	} | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 184 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 185 |  |  | 	private function initFtsFilterState() { | 
            
                                                                        
                            
            
                                    
            
            
                | 186 |  |  | 		return [ | 
            
                                                                        
                            
            
                                    
            
            
                | 187 |  |  | 			'message_classes' => [], | 
            
                                                                        
                            
            
                                    
            
            
                | 188 |  |  | 			'date_start' => null, | 
            
                                                                        
                            
            
                                    
            
            
                | 189 |  |  | 			'date_end' => null, | 
            
                                                                        
                            
            
                                    
            
            
                | 190 |  |  | 			'unread' => false, | 
            
                                                                        
                            
            
                                    
            
            
                | 191 |  |  | 			'has_attachments' => false, | 
            
                                                                        
                            
            
                                    
            
            
                | 192 |  |  | 		]; | 
            
                                                                        
                            
            
                                    
            
            
                | 193 |  |  | 	} | 
            
                                                                        
                            
            
                                    
            
            
                | 194 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 195 |  |  | 	private function mergeFtsFilterState(array $base, array $delta) { | 
            
                                                                        
                            
            
                                    
            
            
                | 196 |  |  | 		$base['message_classes'] = array_merge($base['message_classes'], $delta['message_classes']); | 
            
                                                                        
                            
            
                                    
            
            
                | 197 |  |  | 		if ($delta['date_start'] !== null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 198 |  |  | 			$base['date_start'] = $base['date_start'] === null ? $delta['date_start'] : max($base['date_start'], $delta['date_start']); | 
            
                                                                        
                            
            
                                    
            
            
                | 199 |  |  | 		} | 
            
                                                                        
                            
            
                                    
            
            
                | 200 |  |  | 		if ($delta['date_end'] !== null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 201 |  |  | 			$base['date_end'] = $base['date_end'] === null ? $delta['date_end'] : min($base['date_end'], $delta['date_end']); | 
            
                                                                        
                            
            
                                    
            
            
                | 202 |  |  | 		} | 
            
                                                                        
                            
            
                                    
            
            
                | 203 |  |  | 		$base['unread'] = $base['unread'] || $delta['unread']; | 
            
                                                                        
                            
            
                                    
            
            
                | 204 |  |  | 		$base['has_attachments'] = $base['has_attachments'] || $delta['has_attachments']; | 
            
                                                                        
                            
            
                                    
            
            
                | 205 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 206 |  |  | 		return $base; | 
            
                                                                        
                            
            
                                    
            
            
                | 207 |  |  | 	} | 
            
                                                                        
                            
            
                                    
            
            
                | 208 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 209 |  |  | 	private function buildFtsDescriptor($restriction) { | 
            
                                                                        
                            
            
                                    
            
            
                | 210 |  |  | 		[$ast, $filters] = $this->convertRestrictionToAst($restriction); | 
            
                                                                        
                            
            
                                    
            
            
                | 211 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 212 |  |  | 		$filters['message_classes'] = array_values(array_unique($filters['message_classes'])); | 
            
                                                                        
                            
            
                                    
            
            
                | 213 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 214 |  |  | 		return [ | 
            
                                                                        
                            
            
                                    
            
            
                | 215 |  |  | 			'ast' => $ast, | 
            
                                                                        
                            
            
                                    
            
            
                | 216 |  |  | 			'message_classes' => $filters['message_classes'], | 
            
                                                                        
                            
            
                                    
            
            
                | 217 |  |  | 			'date_start' => $filters['date_start'], | 
            
                                                                        
                            
            
                                    
            
            
                | 218 |  |  | 			'date_end' => $filters['date_end'], | 
            
                                                                        
                            
            
                                    
            
            
                | 219 |  |  | 			'unread' => $filters['unread'], | 
            
                                                                        
                            
            
                                    
            
            
                | 220 |  |  | 			'has_attachments' => $filters['has_attachments'], | 
            
                                                                        
                            
            
                                    
            
            
                | 221 |  |  | 		]; | 
            
                                                                        
                            
            
                                    
            
            
                | 222 |  |  | 	} | 
            
                                                                        
                            
            
                                    
            
            
                | 223 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 224 |  |  | 	private function convertRestrictionToAst($restriction, $context = null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 225 |  |  | 		$filters = $this->initFtsFilterState(); | 
            
                                                                        
                            
            
                                    
            
            
                | 226 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 227 |  |  | 		if (!is_array($restriction) || empty($restriction)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 228 |  |  | 			return [null, $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 229 |  |  | 		} | 
            
                                                                        
                            
            
                                    
            
            
                | 230 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 231 |  |  | 		$type = $restriction[0]; | 
            
                                                                        
                            
            
                                    
            
            
                | 232 |  |  | 		switch ($type) { | 
            
                                                                        
                            
            
                                    
            
            
                | 233 |  |  | 			case RES_AND: | 
            
                                                                        
                            
            
                                    
            
            
                | 234 |  |  | 			case RES_OR: | 
            
                                                                        
                            
            
                                    
            
            
                | 235 |  |  | 				$children = []; | 
            
                                                                        
                            
            
                                    
            
            
                | 236 |  |  | 				$subRestrictions = $restriction[1] ?? []; | 
            
                                                                        
                            
            
                                    
            
            
                | 237 |  |  | 				if (is_array($subRestrictions)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 238 |  |  | 					foreach ($subRestrictions as $subRestriction) { | 
            
                                                                        
                            
            
                                    
            
            
                | 239 |  |  | 						[$childAst, $childFilters] = $this->convertRestrictionToAst($subRestriction, $context); | 
            
                                                                        
                            
            
                                    
            
            
                | 240 |  |  | 						$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                        
                            
            
                                    
            
            
                | 241 |  |  | 						if ($childAst !== null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 242 |  |  | 							$children[] = $childAst; | 
            
                                                                        
                            
            
                                    
            
            
                | 243 |  |  | 						} | 
            
                                                                        
                            
            
                                    
            
            
                | 244 |  |  | 					} | 
            
                                                                        
                            
            
                                    
            
            
                | 245 |  |  | 				} | 
            
                                                                        
                            
            
                                    
            
            
                | 246 |  |  | 				if (empty($children)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 247 |  |  | 					return [null, $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 248 |  |  | 				} | 
            
                                                                        
                            
            
                                    
            
            
                | 249 |  |  | 				if (count($children) === 1) { | 
            
                                                                        
                            
            
                                    
            
            
                | 250 |  |  | 					return [$children[0], $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 251 |  |  | 				} | 
            
                                                                        
                            
            
                                    
            
            
                | 252 |  |  | 				return [[ | 
            
                                                                        
                            
            
                                    
            
            
                | 253 |  |  | 					'op' => $type == RES_AND ? 'AND' : 'OR', | 
            
                                                                        
                            
            
                                    
            
            
                | 254 |  |  | 					'children' => $children, | 
            
                                                                        
                            
            
                                    
            
            
                | 255 |  |  | 				], $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 256 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 257 |  |  | 			case RES_NOT: | 
            
                                                                        
                            
            
                                    
            
            
                | 258 |  |  | 				$sub = $restriction[1][0] ?? null; | 
            
                                                                        
                            
            
                                    
            
            
                | 259 |  |  | 				[$childAst, $childFilters] = $this->convertRestrictionToAst($sub, $context); | 
            
                                                                        
                            
            
                                    
            
            
                | 260 |  |  | 				$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                        
                            
            
                                    
            
            
                | 261 |  |  | 				if ($childAst === null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 262 |  |  | 					return [null, $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 263 |  |  | 				} | 
            
                                                                        
                            
            
                                    
            
            
                | 264 |  |  | 				return [[ | 
            
                                                                        
                            
            
                                    
            
            
                | 265 |  |  | 					'op' => 'NOT', | 
            
                                                                        
                            
            
                                    
            
            
                | 266 |  |  | 					'children' => [$childAst], | 
            
                                                                        
                            
            
                                    
            
            
                | 267 |  |  | 				], $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 268 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 269 |  |  | 			case RES_CONTENT: | 
            
                                                                        
                            
            
                                    
            
            
                | 270 |  |  | 				$subres = $restriction[1]; | 
            
                                                                        
                            
            
                                    
            
            
                | 271 |  |  | 				$propTag = $subres[ULPROPTAG] ?? null; | 
            
                                                                        
                            
            
                                    
            
            
                | 272 |  |  | 				if ($propTag === null) { | 
            
                                                                        
                            
            
                                    
            
            
                | 273 |  |  | 					return [null, $filters]; | 
            
                                                                        
                            
            
                                    
            
            
                | 274 |  |  | 				} | 
            
                                                                        
                            
            
                                    
            
            
                | 275 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 276 |  |  | 				if ($propTag == PR_MESSAGE_CLASS) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 277 |  |  | 					$value = $subres[VALUE][$propTag] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 278 |  |  | 					if ($value !== null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 279 |  |  | 						$filters['message_classes'][] = $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 280 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 281 |  |  | 					return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 282 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 283 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 284 |  |  | 				$fields = $this->mapPropTagToFtsFields($propTag, $context); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 285 |  |  | 				$value = $subres[VALUE][$propTag] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 286 |  |  | 				if (empty($fields) || $value === null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 287 |  |  | 					return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 288 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 289 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 290 |  |  | 				$terms = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 291 |  |  | 				if (is_array($value)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 292 |  |  | 					foreach ($value as $entry) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 293 |  |  | 						if ($entry !== '' && $entry !== null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 294 |  |  | 							$terms[] = [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 295 |  |  | 								'type' => 'term', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 296 |  |  | 								'fields' => $fields, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 297 |  |  | 								'value' => (string) $entry, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 298 |  |  | 							]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 299 |  |  | 						} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 300 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 301 |  |  | 				} else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 302 |  |  | 					$terms[] = [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 303 |  |  | 						'type' => 'term', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 304 |  |  | 						'fields' => $fields, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 305 |  |  | 						'value' => (string) $value, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 306 |  |  | 					]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 307 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 308 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 309 |  |  | 				if (empty($terms)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 310 |  |  | 					return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 311 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 312 |  |  | 				if (count($terms) === 1) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 313 |  |  | 					return [$terms[0], $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 314 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 315 |  |  | 				return [[ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 316 |  |  | 					'op' => 'OR', | 
            
                                                                                                            
                            
            
                                    
            
            
                | 317 |  |  | 					'children' => $terms, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 318 |  |  | 				], $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 319 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 320 |  |  | 			case RES_PROPERTY: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 321 |  |  | 				$subres = $restriction[1]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 322 |  |  | 				$propTag = $subres[ULPROPTAG] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 323 |  |  | 				if ($propTag === null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 324 |  |  | 					return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 325 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 326 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 327 |  |  | 				if ($propTag == PR_MESSAGE_DELIVERY_TIME || $propTag == PR_LAST_MODIFICATION_TIME) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 328 |  |  | 					$value = $subres[VALUE][$propTag] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 329 |  |  | 					if ($value !== null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 330 |  |  | 						if ($subres[RELOP] == RELOP_LT || $subres[RELOP] == RELOP_LE) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 331 |  |  | 							$filters['date_end'] = $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 332 |  |  | 						} elseif ($subres[RELOP] == RELOP_GT || $subres[RELOP] == RELOP_GE) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 333 |  |  | 							$filters['date_start'] = $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 334 |  |  | 						} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 335 |  |  | 					} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 336 |  |  | 					return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 337 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 338 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 339 |  |  | 				if (isset($this->properties['hide_attachments']) && $propTag == $this->properties['hide_attachments']) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 340 |  |  | 					$filters['has_attachments'] = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 341 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 342 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 343 |  |  | 				return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 344 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 345 |  |  | 			case RES_BITMASK: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 346 |  |  | 				$subres = $restriction[1]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 347 |  |  | 				if (($subres[ULPROPTAG] ?? null) == PR_MESSAGE_FLAGS && ($subres[ULTYPE] ?? null) == BMR_EQZ) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 348 |  |  | 					$filters['unread'] = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 349 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 350 |  |  | 				return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 351 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 352 |  |  | 			case RES_SUBRESTRICTION: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 353 |  |  | 				$subres = $restriction[1]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 354 |  |  | 				$propTag = $subres[ULPROPTAG] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 355 |  |  | 				if ($propTag == PR_MESSAGE_ATTACHMENTS) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 356 |  |  | 					$filters['has_attachments'] = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 357 |  |  | 					$inner = $subres[RESTRICTION] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 358 |  |  | 					[$childAst, $childFilters] = $this->convertRestrictionToAst($inner, 'attachments'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 359 |  |  | 					$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 360 |  |  | 					return [$childAst, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 361 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 362 |  |  | 				if ($propTag == PR_MESSAGE_RECIPIENTS) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 363 |  |  | 					$inner = $subres[RESTRICTION] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 364 |  |  | 					[$childAst, $childFilters] = $this->convertRestrictionToAst($inner, 'recipients'); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 365 |  |  | 					$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 366 |  |  | 					return [$childAst, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 367 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 368 |  |  | 				$inner = $subres[RESTRICTION] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 369 |  |  | 				[$childAst, $childFilters] = $this->convertRestrictionToAst($inner, $context); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 370 |  |  | 				$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 371 |  |  | 				return [$childAst, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 372 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 373 |  |  | 			case RES_COMMENT: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 374 |  |  | 				$inner = $restriction[1][RESTRICTION] ?? null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 375 |  |  | 				[$childAst, $childFilters] = $this->convertRestrictionToAst($inner, $context); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 376 |  |  | 				$filters = $this->mergeFtsFilterState($filters, $childFilters); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 377 |  |  | 				return [$childAst, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 378 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 379 |  |  | 			default: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 380 |  |  | 				return [null, $filters]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 381 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 382 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 383 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 384 |  |  | 	private function mapPropTagToFtsFields($propTag, $context = null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 385 |  |  | 		if ($context === 'attachments') { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 386 |  |  | 			return ['attachments']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 387 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 388 |  |  | 		if ($context === 'recipients') { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 389 |  |  | 			return ['recipients']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 390 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 391 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 392 |  |  | 		static $map = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 393 |  |  | 		if ($map === null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 394 |  |  | 			$map = [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 395 |  |  | 				PR_SUBJECT => ['subject'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 396 |  |  | 				PR_BODY => ['content', 'attachments'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 397 |  |  | 				PR_SENDER_NAME => ['sender'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 398 |  |  | 				PR_SENDER_EMAIL_ADDRESS => ['sender'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 399 |  |  | 				PR_SENT_REPRESENTING_NAME => ['sending'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 400 |  |  | 				PR_SENT_REPRESENTING_EMAIL_ADDRESS => ['sending'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 401 |  |  | 				PR_DISPLAY_TO => ['recipients'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 402 |  |  | 				PR_DISPLAY_CC => ['recipients'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 403 |  |  | 				PR_DISPLAY_BCC => ['recipients'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 404 |  |  | 				PR_EMAIL_ADDRESS => ['recipients'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 405 |  |  | 				PR_SMTP_ADDRESS => ['recipients'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 406 |  |  | 				PR_DISPLAY_NAME => ['others'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 407 |  |  | 				PR_ATTACH_LONG_FILENAME => ['attachments'], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 408 |  |  | 			]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 409 |  |  | 			if (defined('PR_NORMALIZED_SUBJECT')) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 410 |  |  | 				$map[PR_NORMALIZED_SUBJECT] = ['subject']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 411 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 412 |  |  | 			if (isset($this->properties['categories'])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 413 |  |  | 				$map[$this->properties['categories']] = ['others']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 414 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 415 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 416 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 417 |  |  | 		return $map[$propTag] ?? []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 418 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 419 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 420 |  |  | 	/** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 421 |  |  | 	 *	Function will set search restrictions on search folder and start search process | 
            
                                                                                                            
                            
            
                                    
            
            
                | 422 |  |  | 	 *	and it will also parse visible columns and sorting data when sending results to client. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 423 |  |  | 	 * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 424 |  |  | 	 * @param object $store      MAPI Message Store Object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 425 |  |  | 	 * @param string $entryid    entryid of the folder | 
            
                                                                                                            
                            
            
                                    
            
            
                | 426 |  |  | 	 * @param object $action     the action data, sent by the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 427 |  |  | 	 * @param string $actionType the action type, sent by the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 428 |  |  | 	 */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 429 |  |  | 	#[Override] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 430 |  |  | 	public function search($store, $entryid, $action, $actionType) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 431 |  |  | 		$useSearchFolder = $action["use_searchfolder"] ?? false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 432 |  |  | 		$this->logFtsDebug('Search requested', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 433 |  |  | 			'store_entryid' => $action['store_entryid'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 434 |  |  | 			'entryid' => $this->formatEntryIdForLog($entryid), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 435 |  |  | 			'subfolders' => $action['subfolders'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 436 |  |  | 			'use_searchfolder' => (bool) $useSearchFolder, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 437 |  |  | 			'restriction_present' => array_key_exists('restriction', $action), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 438 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 439 |  |  | 		if (!$useSearchFolder) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 440 |  |  | 			$this->logFtsDebug('Search fallback: store does not support search folders', []); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 441 |  |  | 			/* | 
            
                                                                                                            
                            
            
                                    
            
            
                | 442 |  |  | 			 * store doesn't support search folders so we can't use this | 
            
                                                                                                            
                            
            
                                    
            
            
                | 443 |  |  | 			 * method instead we will pass restriction to messageList and | 
            
                                                                                                            
                            
            
                                    
            
            
                | 444 |  |  | 			 * it will give us the restricted results | 
            
                                                                                                            
                            
            
                                    
            
            
                | 445 |  |  | 			 */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 446 |  |  | 			return parent::messageList($store, $entryid, $action, "list"); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 447 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 448 |  |  | 		$store_props = mapi_getprops($store, [PR_MDB_PROVIDER, PR_DEFAULT_STORE, PR_IPM_SUBTREE_ENTRYID]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 449 |  |  | 		$this->logFtsDebug('Resolved store properties for search', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 450 |  |  | 			'provider' => isset($store_props[PR_MDB_PROVIDER]) ? bin2hex((string) $store_props[PR_MDB_PROVIDER]) : null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 451 |  |  | 			'default_store' => $store_props[PR_DEFAULT_STORE] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 452 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 453 |  |  | 		if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_PUBLIC_GUID) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 454 |  |  | 			$this->logFtsDebug('Search fallback: public store does not support search folders', []); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 455 |  |  | 			// public store does not support search folders | 
            
                                                                                                            
                            
            
                                    
            
            
                | 456 |  |  | 			return parent::messageList($store, $entryid, $action, "search"); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 457 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 458 |  |  | 		if ($GLOBALS['entryid']->compareEntryIds(bin2hex($entryid), bin2hex(TodoList::getEntryId()))) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 459 |  |  | 			$this->logFtsDebug('Search fallback: todo list uses legacy restriction path', []); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 460 |  |  | 			// todo list do not need to perform full text index search | 
            
                                                                                                            
                            
            
                                    
            
            
                | 461 |  |  | 			return parent::messageList($store, $entryid, $action, "list"); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 462 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 463 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 464 |  |  | 		$this->searchFolderList = true; // Set to indicate this is not the normal folder, but a search folder | 
            
                                                                                                            
                            
            
                                    
            
            
                | 465 |  |  | 		$this->restriction = false; | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 466 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 467 |  |  | 		// Parse Restriction | 
            
                                                                                                            
                            
            
                                    
            
            
                | 468 |  |  | 		$this->parseRestriction($action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 469 |  |  | 		if ($this->restriction == false) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 470 |  |  | 			$this->logFtsDebug('Restriction parsing failed', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 471 |  |  | 				'action_type' => $actionType, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 472 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 473 |  |  | 			// if error in creating restriction then send error to client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 474 |  |  | 			$errorInfo = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 475 |  |  | 			$errorInfo["error_message"] = _("Error in search, please try again") . "."; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 476 |  |  | 			$errorInfo["original_error_message"] = "Error in parsing restrictions."; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 477 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 478 |  |  | 			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                            
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 479 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 480 |  |  | 		$ftsDescriptor = $this->buildFtsDescriptor($this->restriction); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 481 |  |  | 		if (empty($ftsDescriptor['ast'])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 482 |  |  | 			$this->logFtsDebug('Failed to translate restriction to FTS descriptor', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 483 |  |  | 				'restriction_sample' => array_keys($this->restriction), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 484 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 485 |  |  | 			$errorInfo = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 486 |  |  | 			$errorInfo["error_message"] = _("Error in search, please try again") . "."; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 487 |  |  | 			$errorInfo["original_error_message"] = "Unable to translate search query to full-text expression."; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 488 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 489 |  |  | 			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 490 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 491 |  |  | 		$serializedRestriction = serialize($this->restriction); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 492 |  |  | 		$restrictionSignature = md5($serializedRestriction); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 493 |  |  | 		$this->logFtsDebug('Restriction parsed for full-text search', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 494 |  |  | 			'restriction_signature' => $restrictionSignature, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 495 |  |  | 			'fts_descriptor' => $ftsDescriptor, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 496 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 497 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 498 |  |  | 		$isSetSearchFolderEntryId = isset($action['search_folder_entryid']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 499 |  |  | 		if ($isSetSearchFolderEntryId) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 500 |  |  | 			$this->sessionData['searchFolderEntryId'] = $action['search_folder_entryid']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 501 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 502 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 503 |  |  | 		if (isset($action['forceCreateSearchFolder']) && $action['forceCreateSearchFolder']) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 504 |  |  | 			$isSetSearchFolderEntryId = false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 505 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 506 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 507 |  |  | 		// create or open search folder | 
            
                                                                                                            
                            
            
                                    
            
            
                | 508 |  |  | 		$searchFolder = $this->createSearchFolder($store, $isSetSearchFolderEntryId); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 509 |  |  | 		if ($searchFolder === false) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 510 |  |  | 			if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 511 |  |  | 				$this->messageList($store, $entryid, $action, "search"); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 512 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 513 |  |  | 				return true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 514 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 515 |  |  | 			// if error in creating search folder then send error to client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 516 |  |  | 			$errorInfo = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 517 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 518 |  |  | 			$errorInfo["error_message"] = match (mapi_last_hresult()) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 519 |  |  | 				MAPI_E_NO_ACCESS => _("Unable to perform search query, no permissions to create search folder."), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 520 |  |  | 				MAPI_E_NOT_FOUND => _("Unable to perform search query, search folder not found."), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 521 |  |  | 				default => _("Unable to perform search query, store might not support searching."), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 522 |  |  | 			}; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 523 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 524 |  |  | 			$errorInfo["original_error_message"] = _("Error in creating search folder."); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 525 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 526 |  |  | 			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 527 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 528 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 529 |  |  | 		$subfolder_flag = 0; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 530 |  |  | 		$recursive = false; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 531 |  |  | 		if (isset($action["subfolders"]) && $action["subfolders"] == "true") { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 532 |  |  | 			$recursive = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 533 |  |  | 			$subfolder_flag = RECURSIVE_SEARCH; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 534 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 535 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 536 |  |  | 		if (!is_array($entryid)) { | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 537 |  |  | 			$entryids = [$entryid]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 538 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 539 |  |  | 		else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 540 |  |  | 			$entryids = $entryid; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 541 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 542 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 543 |  |  | 		$searchFolderEntryId = $this->sessionData['searchFolderEntryId']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 544 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 545 |  |  | 		// check if searchcriteria has changed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 546 |  |  | 		$restrictionCheck = md5($serializedRestriction . $searchFolderEntryId . $subfolder_flag); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 547 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 548 |  |  | 		// check if there is need to set searchcriteria again | 
            
                                                                                                            
                            
            
                                    
            
            
                | 549 |  |  | 		if (!isset($this->sessionData['searchCriteriaCheck']) || $restrictionCheck != $this->sessionData['searchCriteriaCheck']) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 550 |  |  | 			if (!empty($this->sessionData['searchOriginalEntryids']) && | 
            
                                                                                                            
                            
            
                                    
            
            
                | 551 |  |  | 				isset($action['entryid']) && | 
            
                                                                                                            
                            
            
                                    
            
            
                | 552 |  |  | 				in_array($action['entryid'], $this->sessionData['searchOriginalEntryids']) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 553 |  |  | 			) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 554 |  |  | 				// get entryids of original folders, and use it to set new search criteria | 
            
                                                                                                            
                            
            
                                    
            
            
                | 555 |  |  | 				$entryids = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 556 |  |  | 				$entryIdsCount = count($this->sessionData['searchOriginalEntryids']); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 557 |  |  | 				for ($index = 0; $index < $entryIdsCount; ++$index) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 558 |  |  | 					$entryids[] = hex2bin((string) $this->sessionData['searchOriginalEntryids'][$index]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 559 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 560 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 561 |  |  | 			else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 562 |  |  | 				// store entryids of original folders, so that can be used for re-setting the search criteria if needed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 563 |  |  | 				$this->sessionData['searchOriginalEntryids'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 564 |  |  | 				for ($index = 0, $len = count($entryids); $index < $len; ++$index) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 565 |  |  | 					$this->sessionData['searchOriginalEntryids'][] = bin2hex($entryids[$index]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 566 |  |  | 				} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 567 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 568 |  |  | 			// we never start the search folder because we will populate the search folder by ourselves | 
            
                                                                                                            
                            
            
                                    
            
            
                | 569 |  |  | 			mapi_folder_setsearchcriteria($searchFolder, $this->restriction, $entryids, $subfolder_flag | STOP_SEARCH); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 570 |  |  | 			$this->logFtsDebug('Search criteria updated', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 571 |  |  | 				'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 572 |  |  | 				'restriction_signature' => $restrictionSignature, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 573 |  |  | 				'recursive' => $recursive, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 574 |  |  | 				'scope_entryids' => $this->formatEntryIdForLog($entryids), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 575 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 576 |  |  | 			$this->sessionData['searchCriteriaCheck'] = $restrictionCheck; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 577 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 578 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 579 |  |  | 		if (isset($this->sessionData['searchCriteriaCheck']) || $restrictionCheck == $this->sessionData['searchCriteriaCheck']) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 580 |  |  | 			$folderEntryid = bin2hex($entryid); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 581 |  |  | 			if ($this->sessionData['searchOriginalEntryids'][0] !== $folderEntryid) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 582 |  |  | 				$this->sessionData['searchOriginalEntryids'][0] = $folderEntryid; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 583 |  |  | 				// we never start the search folder because we will populate the search folder by ourselves | 
            
                                                                                                            
                            
            
                                    
            
            
                | 584 |  |  | 				mapi_folder_setsearchcriteria($searchFolder, $this->restriction, [$entryid], $subfolder_flag | STOP_SEARCH); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 585 |  |  | 				$this->logFtsDebug('Search criteria refreshed for active folder', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 586 |  |  | 					'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 587 |  |  | 					'restriction_signature' => $restrictionSignature, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 588 |  |  | 					'target_entryid' => $this->formatEntryIdForLog($entryid), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 589 |  |  | 					'recursive' => $recursive, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 590 |  |  | 				]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 591 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 592 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 593 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 594 |  |  | 		// Sort | 
            
                                                                                                            
                            
            
                                    
            
            
                | 595 |  |  | 		$this->parseSortOrder($action); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 596 |  |  | 		// Initialize search patterns with default values | 
            
                                                                                                            
                            
            
                                    
            
            
                | 597 |  |  | 		if (is_array($ftsDescriptor['message_classes']) && | 
            
                                                                                                            
                            
            
                                    
            
            
                | 598 |  |  | 			count($ftsDescriptor['message_classes']) >= 7) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 599 |  |  | 			$ftsDescriptor['message_classes'] = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 600 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 601 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 602 |  |  | 		$username = null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 603 |  |  | 		if ($store_props[PR_MDB_PROVIDER] == ZARAFA_STORE_DELEGATE_GUID) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 604 |  |  | 			$eidObj = $GLOBALS["entryid"]->createMsgStoreEntryIdObj(hex2bin((string) $action['store_entryid'])); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 605 |  |  | 			$username = $eidObj['ServerShortname']; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 606 |  |  | 			$session = $GLOBALS["mapisession"]->getSession(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 607 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 608 |  |  | 			if ($username) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 609 |  |  | 				$indexDB = new IndexSqlite($username, $session, $store); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 610 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 611 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 612 |  |  | 		else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 613 |  |  | 			$indexDB = new IndexSqlite(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 614 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 615 |  |  | 		$this->logFtsDebug('Dispatching search to index backend', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 616 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 617 |  |  | 			'restriction_signature' => $restrictionSignature, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 618 |  |  | 			'recursive' => $recursive, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 619 |  |  | 			'delegate_username' => $username, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 620 |  |  | 			'message_classes' => $ftsDescriptor['message_classes'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 621 |  |  | 			'filters' => [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 622 |  |  | 				'date_start' => $ftsDescriptor['date_start'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 623 |  |  | 				'date_end' => $ftsDescriptor['date_end'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 624 |  |  | 				'unread' => !empty($ftsDescriptor['unread']), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 625 |  |  | 				'has_attachments' => !empty($ftsDescriptor['has_attachments']), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 626 |  |  | 			], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 627 |  |  | 			'ast' => $ftsDescriptor['ast'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 628 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 629 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 630 |  |  | 		$search_result = $indexDB->search(hex2bin((string) $searchFolderEntryId), $ftsDescriptor, $entryid, $recursive); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 631 |  |  | 		if ($search_result == false) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 632 |  |  | 			$this->logFtsDebug('Index search returned no data', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 633 |  |  | 				'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 634 |  |  | 				'restriction_signature' => $restrictionSignature, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 635 |  |  | 				'recursive' => $recursive, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 636 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 637 |  |  | 			$errorInfo = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 638 |  |  | 			$errorInfo["error_message"] = _("Unable to perform search query, store might not support searching."); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 639 |  |  | 			$errorInfo["original_error_message"] = "Full-text search backend returned no result set."; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 640 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 641 |  |  | 			return $this->sendSearchErrorToClient($store, $entryid, $action, $errorInfo); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 642 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 643 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 644 |  |  | 		unset($action["restriction"]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 645 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 646 |  |  | 		// Get the table and merge the arrays | 
            
                                                                                                            
                            
            
                                    
            
            
                | 647 |  |  | 		$table = $GLOBALS["operations"]->getTable($store, hex2bin((string) $searchFolderEntryId), $this->properties, $this->sort, $this->start); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 648 |  |  | 		$this->logFtsDebug('Search folder table retrieved', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 649 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 650 |  |  | 			'table_item_count' => isset($table['item']) ? count($table['item']) : null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 651 |  |  | 			'start' => $this->start, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 652 |  |  | 			'sort' => $this->sort, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 653 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 654 |  |  | 		// Create the data array, which will be send back to the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 655 |  |  | 		$data = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 656 |  |  | 		$data = array_merge($data, $table); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 657 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 658 |  |  | 		$this->getDelegateFolderInfo($store); | 
                            
                    |  |  |  | 
                                                                                        
                                                                                     | 
            
                                                                                                            
                            
            
                                    
            
            
                | 659 |  |  | 		$data = $this->filterPrivateItems($data); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 660 |  |  | 		$this->logFtsDebug('Search results filtered for privacy', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 661 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 662 |  |  | 			'item_count_after_filter' => isset($data['item']) ? count($data['item']) : null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 663 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 664 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 665 |  |  | 		// remember which entryid's are send to the client | 
            
                                                                                                            
                            
            
                                    
            
            
                | 666 |  |  | 		$searchResults = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 667 |  |  | 		foreach ($table["item"] as $item) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 668 |  |  | 			// store entryid => last_modification_time mapping | 
            
                                                                                                            
                            
            
                                    
            
            
                | 669 |  |  | 			$searchResults[$item["entryid"]] = $item["props"]["last_modification_time"]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 670 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 671 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 672 |  |  | 		// store search results into session data | 
            
                                                                                                            
                            
            
                                    
            
            
                | 673 |  |  | 		if (!isset($this->sessionData['searchResults'])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 674 |  |  | 			$this->sessionData['searchResults'] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 675 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 676 |  |  | 		$this->sessionData['searchResults'][$searchFolderEntryId] = $searchResults; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 677 |  |  | 		$this->logFtsDebug('Search results stored in session', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 678 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 679 |  |  | 			'result_count' => count($searchResults), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 680 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 681 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 682 |  |  | 		$result = mapi_folder_getsearchcriteria($searchFolder); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 683 |  |  | 		$this->logFtsDebug('Search folder state retrieved', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 684 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 685 |  |  | 			'searchstate' => $result['searchstate'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 686 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 687 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 688 |  |  | 		$data["search_meta"] = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 689 |  |  | 		$data["search_meta"]["searchfolder_entryid"] = $searchFolderEntryId; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 690 |  |  | 		$data["search_meta"]["search_store_entryid"] = $action["store_entryid"]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 691 |  |  | 		$data["search_meta"]["searchstate"] = $result["searchstate"]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 692 |  |  | 		$data["search_meta"]["results"] = count($searchResults); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 693 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 694 |  |  | 		// Reopen the search folder, because otherwise the suggestion property will | 
            
                                                                                                            
                            
            
                                    
            
            
                | 695 |  |  | 		// not have been updated | 
            
                                                                                                            
                            
            
                                    
            
            
                | 696 |  |  | 		$searchFolder = $this->createSearchFolder($store, true); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 697 |  |  | 		$storeProps = mapi_getprops($searchFolder, [PR_EC_SUGGESTION]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 698 |  |  | 		if (isset($storeProps[PR_EC_SUGGESTION])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 699 |  |  | 			$data["search_meta"]["suggestion"] = $storeProps[PR_EC_SUGGESTION]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 700 |  |  | 			$this->logFtsDebug('Search suggestion ready', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 701 |  |  | 				'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 702 |  |  | 				'suggestion' => $storeProps[PR_EC_SUGGESTION], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 703 |  |  | 			]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 704 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 705 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 706 |  |  | 		$this->addActionData("search", $data); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 707 |  |  | 		$GLOBALS["bus"]->addData($this->getResponseData()); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 708 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 709 |  |  | 		$this->logFtsDebug('Search response dispatched to client', [ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 710 |  |  | 			'search_folder_entryid' => $searchFolderEntryId, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 711 |  |  | 			'items_returned' => isset($data['item']) ? count($data['item']) : null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 712 |  |  | 			'search_meta' => $data['search_meta'] ?? null, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 713 |  |  | 		]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 714 |  |  | 		return true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 715 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 716 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 717 |  |  | 	private function logFtsDebug(string $message, array $context = []): void { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 718 |  |  | 		if (!DEBUG_FULLTEXT_SEARCH) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 719 |  |  | 			return; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 720 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 721 |  |  | 		$prefix = '[fts-debug][module] '; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 722 |  |  | 		if (!empty($context)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 723 |  |  | 			$encoded = json_encode($context, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 724 |  |  | 			if ($encoded === false) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 725 |  |  | 				$encoded = 'context_encoding_failed'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 726 |  |  | 			} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 727 |  |  | 			error_log($prefix . $message . ' ' . $encoded); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 728 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 729 |  |  | 		else { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 730 |  |  | 			error_log($prefix . $message); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 731 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 732 |  |  | 	} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 733 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 734 |  |  | 	private function formatEntryIdForLog($entryid) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 735 |  |  | 		if ($entryid === null) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 736 |  |  | 			return null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 737 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 738 |  |  | 		if (is_array($entryid)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 739 |  |  | 			return array_map([$this, 'formatEntryIdForLog'], $entryid); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 740 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 741 |  |  | 		if (!is_string($entryid)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 742 |  |  | 			return $entryid; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 743 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 744 |  |  | 		if ($entryid === '') { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 745 |  |  | 			return ''; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 746 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 747 |  |  | 		if (preg_match('/[^\x20-\x7E]/', $entryid)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 748 |  |  | 			return bin2hex($entryid); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 749 |  |  | 		} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 750 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 751 |  |  | 		return $entryid; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 752 |  |  | 	} | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 753 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 754 |  |  |  |