ECSearchFolders.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #ifndef ECSEARCHFOLDERS_H
  18. #define ECSEARCHFOLDERS_H
  19. #include <kopano/zcdefs.h>
  20. #include <condition_variable>
  21. #include <mutex>
  22. #include <pthread.h>
  23. #include "ECDatabaseFactory.h"
  24. #include <kopano/ECKeyTable.h>
  25. #include "ECStoreObjectTable.h"
  26. #include "soapH.h"
  27. #include "SOAPUtils.h"
  28. #include <map>
  29. #include <set>
  30. #include <list>
  31. namespace KC {
  32. class ECSessionManager;
  33. struct SEARCHFOLDER _kc_final {
  34. SEARCHFOLDER(unsigned int ulStoreId, unsigned int ulFolderId) {
  35. this->ulStoreId = ulStoreId;
  36. this->ulFolderId = ulFolderId;
  37. memset(&sThreadId, 0, sizeof(sThreadId));
  38. }
  39. ~SEARCHFOLDER() {
  40. if (this->lpSearchCriteria)
  41. FreeSearchCriteria(this->lpSearchCriteria);
  42. }
  43. struct searchCriteria *lpSearchCriteria = nullptr;
  44. pthread_t sThreadId;
  45. std::mutex mMutexThreadFree;
  46. bool bThreadFree = true;
  47. bool bThreadExit = false;
  48. unsigned int ulStoreId;
  49. unsigned int ulFolderId;
  50. };
  51. struct EVENT {
  52. unsigned int ulStoreId;
  53. unsigned int ulFolderId;
  54. unsigned int ulObjectId;
  55. ECKeyTable::UpdateType ulType;
  56. bool operator<(const struct EVENT &b) const { return ulFolderId < b.ulFolderId ? true : (ulType < b.ulType ? true : ( ulObjectId < b.ulObjectId ? true : false ) ); }
  57. bool operator==(const struct EVENT &b) const { return ulFolderId == b.ulFolderId && ulType == b.ulType && ulObjectId == b.ulObjectId; }
  58. };
  59. typedef std::map<unsigned int, SEARCHFOLDER *> FOLDERIDSEARCH;
  60. typedef std::map<unsigned int, FOLDERIDSEARCH> STOREFOLDERIDSEARCH;
  61. typedef std::map<unsigned int, pthread_t> SEARCHTHREADMAP;
  62. struct sSearchFolderStats {
  63. ULONG ulStores;
  64. ULONG ulFolders;
  65. ULONG ulEvents;
  66. ULONGLONG ullSize;
  67. };
  68. /**
  69. * Searchfolder handler
  70. *
  71. * This represents a single manager of all searchfolders on the server; a single thread runs on behalf of this
  72. * manager to handle all object changes, and another thread can be running for each searchfolder that is rebuilding. Most of
  73. * the time only the single update thread is running though.
  74. *
  75. * The searchfolder manager does four things:
  76. * - Loading all searchfolder definitions (restriction and folderlist) at startup
  77. * - Adding and removing searchfolders when users create/remove searchfolders
  78. * - Rebuilding searchfolder contents (when users rebuild searchfolders)
  79. * - Getting searchfolder results (when users open searchfolders)
  80. *
  81. * Storage of searchresults is on-disk in the MySQL database; restarts of the storage server do not affect searchfolders
  82. * except rebuilding searchfolders; when the server starts and finds a searchfolder that was only half-built, a complete
  83. * rebuild is started since we don't know how far the rebuild got last time.
  84. */
  85. class _kc_export ECSearchFolders _kc_final {
  86. public:
  87. _kc_hidden ECSearchFolders(ECSessionManager *, ECDatabaseFactory *);
  88. _kc_hidden virtual ~ECSearchFolders(void);
  89. /**
  90. * Does the initial load of all searchfolders by looking in the hierarchy table for ALL searchfolders and retrieving the
  91. * information for each of them. Will also rebuild folders that need rebuilding (folders with the REBUILDING state)
  92. */
  93. virtual ECRESULT LoadSearchFolders();
  94. /**
  95. * Set search criteria for a new or existing search folder
  96. *
  97. * Will remove any previous search criteria on the folder, cleanup the search results and rebuild the search results.
  98. * This function is called almost directly from the SetSearchCriteria() MAPI function.
  99. *
  100. * @param[in] ulStoreId The store id (hierarchyid) of the searchfolder being modified
  101. * @param[in] ulFolderId The folder id (hierarchyid) of the searchfolder being modified
  102. * @param[in] lpSearchCriteria Search criteria to be set
  103. */
  104. _kc_hidden virtual ECRESULT SetSearchCriteria(unsigned int store_id, unsigned int folder_id, struct searchCriteria *);
  105. /**
  106. * Retrieve search criteria for an existing search folder
  107. *
  108. * @param[in] ulStoreId The store id (hierarchyid) of the searchfolder being modified
  109. * @param[in] ulFolderId The folder id (hierarchyid) of the searchfolder being modified
  110. * @param[out] lpSearchCriteria Search criteria previously set via SetSearchCriteria
  111. */
  112. _kc_hidden virtual ECRESULT GetSearchCriteria(unsigned int store_id, unsigned int folder_id, struct searchCriteria **, unsigned int *search_flags);
  113. /**
  114. * Get current search results for a folder. Simply a database query, nothing more.
  115. *
  116. * This retrieves all the items that the search folder contains as a list of hierarchy IDs. Since the
  117. * search results are already available, the data is returned directly from the database.
  118. *
  119. * @param[in] ulStoreId The store id (hierarchyid) of the searchfolder being modified
  120. * @param[in] ulFolderId The folder id (hierarchyid) of the searchfolder being modified
  121. * @param[out] lstObjIds List of object IDs in the result set. List is cleared and populated.
  122. */
  123. _kc_hidden virtual ECRESULT GetSearchResults(unsigned int store_id, unsigned int folder_id, std::list<unsigned int> *objids);
  124. /**
  125. * Queue a messages change that should be processed to update the search folders
  126. *
  127. * This function should be called for any message object that has been modified so that the change can be processed
  128. * in all searchfolders. You must specify if the item was modified (added) or deleted since delete processing
  129. * is much simpler (just remove the item from all searchfolders)
  130. *
  131. * This function should be called AFTER the change has been written to the database and AFTER the change
  132. * has been comitted, otherwise the change will be invisible to the searchfolder update code.
  133. *
  134. * Folder changes never need to be processed since searchfolders cannot be used for other folders
  135. *
  136. * @param[in] ulStoreId The store id (hierarchyid) of the object that should be processed
  137. * @param[in] ulFolderId The folder id (hierarchyid) of the object that should be processed
  138. * @param[in] ulObjId The hierarchyid of the modified object
  139. * @param[in] ulType ECKeyTable::TABLE_ROW_ADD or TABLE_ROW_MODIFY or TABLE_ROW_DELETE
  140. */
  141. _kc_hidden virtual ECRESULT UpdateSearchFolders(unsigned int store_id, unsigned int folder_id, unsigned int obj_id, ECKeyTable::UpdateType);
  142. /**
  143. * Returns erSuccess if the folder is a search folder
  144. *
  145. * @param[in] ulStoreId The store id (hierarchyid) of the folder being queried
  146. * @param[in] ulFolderId The folder id (hierarchyid) of the folder being queried
  147. */
  148. _kc_hidden virtual ECRESULT IsSearchFolder(unsigned int store_id, unsigned int folder_id);
  149. /**
  150. * Remove a search folder because it has been deleted. Cancels the search before removing the information. It will
  151. * remove all results from the database.
  152. *
  153. * This is differenct from Cancelling a search folder (see CancelSearchFolder()) because the results are actually
  154. * deleted after cancelling.
  155. *
  156. * @param[in] ulStoreId The store id (hierarchyid) of the folder to be removed
  157. * @param[in] ulFolderId The folder id (hierarchyid) of the folder to be removed
  158. */
  159. _kc_hidden virtual ECRESULT RemoveSearchFolder(unsigned int store_id, unsigned int folder_id);
  160. /**
  161. * Remove a search folder of a specific store because it has been deleted. Cancels the search before removing the
  162. * information. It will remove all results from the database.
  163. *
  164. * @param[in] ulStoreId The store id (hierarchyid) of the folder to be removed
  165. */
  166. _kc_hidden virtual ECRESULT RemoveSearchFolder(unsigned int store_id);
  167. /**
  168. * Wait till all threads are down and free the data of a searchfolder
  169. *
  170. * @param[in] lpFolder Search folder data object
  171. */
  172. _kc_hidden void DestroySearchFolder(SEARCHFOLDER *);
  173. /**
  174. * Restart all searches.
  175. * This is a rather heavy operation, and runs synchronously. You have to wait until it has finished.
  176. * This is only called with the --restart-searches option of kopano-server and never used in a running
  177. * system
  178. */
  179. virtual ECRESULT RestartSearches();
  180. /**
  181. * Save search criteria to the database
  182. *
  183. * Purely writes the given search criteria to the database without any further processing. This is really
  184. * a private function but it is used hackishly from ECDatabaseUpdate() when upgrading from really old (4.1)
  185. * versions of kopano which have a slightly different search criteria format. Do not use this function for
  186. * anything else!
  187. *
  188. * @param[in] lpDatabase Database handle
  189. * @param[in] ulStoreId Store id (hierarchy id) of the searchfolder to write
  190. * @param[in] ulFolderId Folder id (hierarchy id) of the searchfolder to write
  191. * @param[in] lpSearchCriteria Search criteria to write
  192. */
  193. _kc_hidden static ECRESULT SaveSearchCriteria(ECDatabase *, unsigned int store_id, unsigned int folder_id, struct searchCriteria *);
  194. /**
  195. * Get the searchfolder statistics
  196. */
  197. _kc_hidden virtual ECRESULT GetStats(sSearchFolderStats &);
  198. /**
  199. * Kick search thread to flush events, and wait for the results.
  200. * Only used in the test protocol.
  201. */
  202. _kc_hidden virtual void FlushAndWait(void);
  203. private:
  204. /**
  205. * Process all events in the queue and remove them from the queue.
  206. *
  207. * Events for changed objects are queued internally and only processed after being flushed here. This function
  208. * groups same-type events together to increase performance because changes in the same folder can be processed
  209. * more efficiently at on time
  210. */
  211. _kc_hidden virtual ECRESULT FlushEvents(void);
  212. /**
  213. * Processes a list of message changes in a single folder that should be processed. This in turn
  214. * will update the search results views through the Table Manager to update the actual user views.
  215. *
  216. * @param[in] ulStoreId Store id of the message changes to be processed
  217. * @param[in] ulFolderId Folder id of the message changes to be processed
  218. * @param[in] lstObjectIDs List of hierarchyids of messages to be processed
  219. * @param[in] ulType Type of change: ECKeyTable::TABLE_ROW_ADD, TABLE_ROW_DELETE or TABLE_ROW_MODIFY
  220. */
  221. _kc_hidden virtual ECRESULT ProcessMessageChange(unsigned int store_id, unsigned int folder_id, ECObjectTableList *objids, ECKeyTable::UpdateType);
  222. /**
  223. * Add a search folder to the list of active searches
  224. *
  225. * This function add a search folder that should be monitored. This means that changes on objects received via UpdateSearchFolders()
  226. * will be matched against the criteria passed to this function and processed accordingly.
  227. *
  228. * Optionally, a rebuild can be started with the fStartSearch flag. This should be done if the search should be rebuilt, or
  229. * if this is a new search folder. On rebuild, existing searches for this search folder will be cancelled first.
  230. *
  231. * @param[in] ulStoreId Store id of the search folder
  232. * @param[in] ulFolderId Folder id of the search folder
  233. * @param[in] fStartSearch TRUE if a rebuild must take place, FALSE if not (eg this happens at server startup)
  234. * @param[in] lpSearchCriteria Search criteria for this search folder
  235. */
  236. _kc_hidden virtual ECRESULT AddSearchFolder(unsigned int store_id, unsigned int folder_id, bool start_search, struct searchCriteria *);
  237. /**
  238. * Cancel a search.
  239. *
  240. * This means that the search results are 'frozen'. If a search thread is running, it is cancelled.
  241. * After a search has been cancelled, we can ignore any updates for that folder, so it is removed from the list
  242. * of active searches. (but the results remain in the database). We also have to remember this fact in the database because
  243. * after a server restart, the search should still be 'stopped' and not rebuilt or active.
  244. *
  245. * @param[in] ulStoreId Store id of the search folder
  246. * @param[in] ulFolderId Folder id of the search folder
  247. */
  248. _kc_hidden virtual ECRESULT CancelSearchFolder(unsigned int store_id, unsigned int folder_id);
  249. /**
  250. * Does an actual search for all matching items for a searchfolder
  251. *
  252. * Adds information in the database, and sends updates through the table manager to
  253. * previously opened tables. This is called only from the search thread and from RestartSearches(). After the
  254. * search is done, changes in the searchfolder are only done incrementally through calls to UpdateSearchFolders().
  255. *
  256. * @param[in] ulStoreId Store id of the search folder
  257. * @param[in] ulFolderId Folder id of the search folder
  258. * @param[in] lpSearchCriteria Search criteria to match
  259. * @param[in] lpbCancel Pointer to cancel flag. This is polled frequently to be able to cancel the search action
  260. * @param[in] bNotify If TRUE, send notifications to table listeners, else do not (eg when doing RestartSearches())
  261. */
  262. _kc_hidden virtual ECRESULT Search(unsigned int store_id, unsigned int folder_id, struct searchCriteria *, bool *cancel, bool notify = true);
  263. /**
  264. * Get the state of a search folder
  265. *
  266. * It may be rebuilding (thread running), running (no thread) or stopped (not active - 'frozen')
  267. *
  268. * @param[in] ulStoreId Store id of the search folder
  269. * @param[in] ulFolderId Folder id of the search folder
  270. * @param[out] lpulState Current state of folder (SEARCH_RUNNING | SEARCH_REBUILD, SEARCH_RUNNING, 0)
  271. */
  272. _kc_hidden virtual ECRESULT GetState(unsigned int store_id, unsigned int folder_id, unsigned int *state);
  273. /**
  274. * Search thread entrypoint.
  275. *
  276. * Simply a wrapper for Search(), and has code to do thread deregistration.
  277. * @param[in] lpParam THREADINFO * thread information
  278. */
  279. _kc_hidden static void *SearchThread(void *);
  280. // Functions to do things in the database
  281. /**
  282. * Reset all results for a searchfolder (removes all results)
  283. *
  284. * @param[in] ulStoreId Store id of the search folder
  285. * @param[in] ulFolderId Folder id of the search folder
  286. */
  287. _kc_hidden virtual ECRESULT ResetResults(unsigned int store_id, unsigned int folder_id);
  288. /**
  289. * Add a search result to a search folder (one message id with flags)
  290. *
  291. * @param[in] ulStoreId Store id of the search folder
  292. * @param[in] ulFolderId Folder id of the search folder
  293. * @param[in] ulObjId Object hierarchy id of the matching message
  294. * @param[in] ulFlags Flags of the object (this should be in-sync with hierarchy table!). May be 0 or MSGFLAG_READ
  295. * @param[out] lpfInserted true if a new record was inserted, false if flags were updated in an existing record
  296. */
  297. _kc_hidden virtual ECRESULT AddResults(unsigned int store_id, unsigned int folder_id, unsigned int obj_id, unsigned int flags, bool *inserted);
  298. /**
  299. * Add multiple search results
  300. *
  301. * @param[in] ulStoreId Store id of the search folder
  302. * @param[in] ulFolderId Folder id of the search folder
  303. * @param[in] ulObjId Object hierarchy id of the matching message
  304. * @param[in] ulFlags Flags of the object (this should be in-sync with hierarchy table!). May be 0 or MSGFLAG_READ
  305. * @param[out] lpulCount Int to be modified with inserted count
  306. * @param[out] lpulUnread Int to be modified with inserted unread count
  307. */
  308. _kc_hidden virtual ECRESULT AddResults(unsigned int store_id, unsigned int folder_id, std::list<unsigned int> &obj_id, std::list<unsigned int> &flags, int *count, int *unread);
  309. /**
  310. * Delete matching results from a search folder
  311. *
  312. * @param[in] ulStoreId Store id of the search folder
  313. * @param[in] ulFolderId Folder id of the search folder
  314. * @param[in] ulObjId Object hierarchy id of the matching message
  315. * @param[out] lpulFlags Flags of the object that was just deleted
  316. */
  317. _kc_hidden virtual ECRESULT DeleteResults(unsigned int store_id, unsigned int folder_id, unsigned int obj_id, unsigned int *flags);
  318. /**
  319. * Set the status of a searchfolder
  320. *
  321. * @param[in] ulFolderId Folder id of the search folder
  322. * @param[in] ulStatus SEARCH_RUNNING or 0
  323. */
  324. _kc_hidden virtual ECRESULT SetStatus(unsigned int folder_id, unsigned int status);
  325. // Functions to load/save search criteria to the database
  326. /**
  327. * Load serialized search criteria from database
  328. *
  329. * @param[in] ulStoreId Store id of the search folder
  330. * @param[in] ulFolderId Folder id of the search folder
  331. * @param[in] lppSearchCriteria Loaded search criteria
  332. */
  333. _kc_hidden virtual ECRESULT LoadSearchCriteria(unsigned int store_id, unsigned int folder_id, struct searchCriteria **);
  334. /**
  335. * Save serialized search criteria to database
  336. *
  337. * @param[in] ulStoreId Store id of the search folder
  338. * @param[in] ulFolderId Folder id of the search folder
  339. * @param[in] lpSearchCriteria Search criteria to save
  340. */
  341. _kc_hidden virtual ECRESULT SaveSearchCriteria(unsigned int store_id, unsigned int folder_id, struct searchCriteria *);
  342. /**
  343. * Main processing thread entrypoint
  344. *
  345. * This thread is running throughout the lifetime of the server and polls the queue periodically to process
  346. * message changes many-at-a-time.
  347. *
  348. * @param[in] lpSearchFolders Pointer to 'this' of search folder manager instance
  349. */
  350. _kc_hidden static void *ProcessThread(void *search_folders);
  351. /**
  352. * Process candidate rows and add them to search folder results
  353. *
  354. * This function processes the list of rows provides against the restriction provides, and
  355. * adds rows to the given folder's result set if the rows match. Each row is evaluated seperately.
  356. *
  357. * @param[in] lpDatabase Database handle
  358. * @param[in] lpSession Session handle
  359. * @param[in] lpRestrict Restriction to match the items with
  360. * @param[in] lpbCancel Pointer to cancellation boolean; processing is stopped when *lpbCancel == true
  361. * @param[in] ulStoreId Store in which the items in ecRows reside
  362. * @param[in] ulFolder The hierarchy of the searchfolder to update with the results
  363. * @param[in] ecODStore Store information
  364. * @param[in] ecRows Rows to evaluate
  365. * @param[in] lpPropTags List of precomputed property tags that are needed to resolve the restriction. The first property in this array MUST be PR_MESSAGE_FLAGS.
  366. * @param[in] locale Locale to use for string comparisons in the restriction
  367. * @param[in] bNotify TRUE on a live system, FALSE if only the database must be updated.
  368. * @return result
  369. */
  370. _kc_hidden virtual ECRESULT ProcessCandidateRows(ECDatabase *, ECSession *, struct restrictTable *r, bool *cancel, unsigned int store_id, unsigned int folder_id, ECODStore *, ECObjectTableList rows, struct propTagArray *tags, const ECLocale &, bool notify);
  371. // Map StoreID -> SearchFolderId -> SearchCriteria
  372. // Because searchfolders only work within a store, this allows us to skip 99% of all
  373. // search folders during UpdateSearchFolders (depending on how many users you have)
  374. std::recursive_mutex m_mutexMapSearchFolders;
  375. STOREFOLDERIDSEARCH m_mapSearchFolders;
  376. // Pthread condition to signal a thread exit
  377. std::condition_variable m_condThreadExited;
  378. ECDatabaseFactory *m_lpDatabaseFactory;
  379. ECSessionManager *m_lpSessionManager;
  380. // List of change events
  381. std::list<EVENT> m_lstEvents;
  382. std::recursive_mutex m_mutexEvents;
  383. std::condition_variable_any m_condEvents;
  384. // Change processing thread
  385. pthread_t m_threadProcess;
  386. // Exit request for processing thread
  387. bool m_bExitThread = false, m_bRunning = false;
  388. };
  389. } /* namespace */
  390. #endif