favoritesutil.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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. #include <kopano/platform.h>
  18. #include <kopano/ECRestriction.h>
  19. #include <kopano/memory.hpp>
  20. #include "favoritesutil.h"
  21. #include <edkmdb.h>
  22. #include <kopano/mapiext.h>
  23. #include <kopano/CommonUtil.h>
  24. #include <kopano/charset/convstring.h>
  25. #include <string>
  26. #include <mapiutil.h>
  27. using namespace std;
  28. using namespace KCHL;
  29. namespace KC {
  30. static constexpr const SizedSPropTagArray(SHORTCUT_NUM, sPropsShortcuts) = {SHORTCUT_NUM, {
  31. PR_INSTANCE_KEY, PR_FAV_PUBLIC_SOURCE_KEY, PR_FAV_PARENT_SOURCE_KEY,
  32. PR_FAV_DISPLAY_NAME, PR_FAV_DISPLAY_ALIAS, PR_FAV_LEVEL_MASK,
  33. PR_FAV_CONTAINER_CLASS}};
  34. const SPropTagArray *GetShortCutTagArray(void)
  35. {
  36. return sPropsShortcuts;
  37. }
  38. /**
  39. * Get the shortcut folder from the users default store.
  40. * If the folder not exist it will create them
  41. *
  42. * @param lpSession Pointer to the current mapi session
  43. * @param lpszFolderName Pointer to a string containing the name for the shorcut folder. If NULL is passed, the folder has the default name 'Shortcut'.
  44. * @param lpszFolderComment Pointer to a string containing a comment associated with the shorcut folder. If NULL is passed, the folder has the default comment 'Shortcut folder'.
  45. * @param ulFlags MAPI_UNICODE unicode properties
  46. * MAPI_CREATE create shorcut folder if not exist
  47. * @param lppShortcutFolder Pointer to a pointer to the opened shortcut folder
  48. */
  49. HRESULT GetShortcutFolder(LPMAPISESSION lpSession, LPTSTR lpszFolderName, LPTSTR lpszFolderComment, ULONG ulFlags, LPMAPIFOLDER* lppShortcutFolder)
  50. {
  51. HRESULT hr = hrSuccess;
  52. memory_ptr<SPropValue> lpPropValue;
  53. object_ptr<IMsgStore> lpMsgStore;
  54. object_ptr<IMAPIFolder> lpFolder;
  55. ULONG ulObjType = 0;
  56. hr = HrOpenDefaultStore(lpSession, &~lpMsgStore);
  57. if(hr != hrSuccess)
  58. return hr;
  59. // Get shortcut entryid
  60. hr = HrGetOneProp(lpMsgStore, PR_IPM_FAVORITES_ENTRYID, &~lpPropValue);
  61. if(hr != hrSuccess) {
  62. if (hr == MAPI_E_NOT_FOUND && ulFlags & MAPI_CREATE)
  63. // Propety not found, re-create the shortcut folder
  64. return CreateShortcutFolder(lpMsgStore, lpszFolderName, lpszFolderComment, ulFlags & MAPI_UNICODE, lppShortcutFolder);
  65. return hr;
  66. }
  67. // Open Shortcut folder
  68. hr = lpMsgStore->OpenEntry(lpPropValue->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpPropValue->Value.bin.lpb), &IID_IMAPIFolder, MAPI_BEST_ACCESS, &ulObjType, &~lpFolder);
  69. if (hr != hrSuccess) {
  70. if (hr == MAPI_E_NOT_FOUND && ulFlags & MAPI_CREATE)
  71. // Folder not found, re-create the shortcut folder
  72. return CreateShortcutFolder(lpMsgStore, lpszFolderName, lpszFolderComment, ulFlags & MAPI_UNICODE, lppShortcutFolder);
  73. return hr;
  74. }
  75. return lpFolder->QueryInterface(IID_IMAPIFolder, reinterpret_cast<void **>(lppShortcutFolder));
  76. }
  77. /**
  78. * Helper function to create a new shorcut folder and update the property
  79. * PR_IPM_FAVORITES_ENTRYID on the store object.
  80. * If the folder already exist, it will used and update only the property
  81. *
  82. * @param lpMsgStore Pointer to the private store
  83. * @param lpszFolderName Pointer to a string containing the name for the shorcut folder. If NULL is passed, the folder has the default name 'Shortcut'.
  84. * @param lpszFolderComment Pointer to a string containing a comment associated with the shorcut folder. If NULL is passed, the folder has the default comment 'Shortcut folder'.
  85. * @param ulFlags MAPI_UNICODE if the folder strings are in unicode format, otherwise 0.
  86. * @param lppShortcutFolder Pointer to a pointer to the opened shortcut folder
  87. */
  88. HRESULT CreateShortcutFolder(IMsgStore *lpMsgStore, LPTSTR lpszFolderName, LPTSTR lpszFolderComment, ULONG ulFlags, LPMAPIFOLDER* lppShortcutFolder)
  89. {
  90. HRESULT hr = hrSuccess;
  91. object_ptr<IMAPIFolder> lpFolder, lpNewFolder;
  92. ULONG ulObjType = 0;
  93. memory_ptr<SPropValue> lpProp;
  94. if (lpMsgStore == nullptr || lppShortcutFolder == nullptr)
  95. return MAPI_E_INVALID_PARAMETER;
  96. if (lpszFolderName == NULL) {
  97. if (ulFlags & MAPI_UNICODE)
  98. lpszFolderName = (LPTSTR)L"Shortcut";
  99. else
  100. lpszFolderName = (LPTSTR)"Shortcut";
  101. }
  102. if (lpszFolderComment == NULL) {
  103. if (ulFlags & MAPI_UNICODE)
  104. lpszFolderComment = (LPTSTR)L"Shortcut folder";
  105. else
  106. lpszFolderComment = (LPTSTR)"Shortcut folder";
  107. }
  108. // Open root folder
  109. hr = lpMsgStore->OpenEntry(0, nullptr, &IID_IMAPIFolder, MAPI_BEST_ACCESS, &ulObjType, &~lpFolder);
  110. if (hr != hrSuccess)
  111. return hr;
  112. hr = lpFolder->CreateFolder(FOLDER_GENERIC, lpszFolderName, lpszFolderComment, &IID_IMAPIFolder, ulFlags | OPEN_IF_EXISTS, &~lpNewFolder);
  113. if (hr != hrSuccess)
  114. return hr;
  115. hr = HrGetOneProp(lpNewFolder, PR_ENTRYID, &~lpProp);
  116. if (hr != hrSuccess)
  117. return hr;
  118. lpProp->ulPropTag = PR_IPM_FAVORITES_ENTRYID;
  119. hr = HrSetOneProp(lpMsgStore, lpProp);
  120. if (hr != hrSuccess)
  121. return hr;
  122. return lpNewFolder->QueryInterface(IID_IMAPIFolder, reinterpret_cast<void **>(lppShortcutFolder));
  123. }
  124. /**
  125. * Remove a favorite in the private store. also the sub favorites will be deleted
  126. *
  127. * @param lpShortcutFolder The shortcut folder in the private store.
  128. * @param lpPropSourceKey Pointer to the sourcekey of a favorite folder
  129. */
  130. HRESULT DelFavoriteFolder(IMAPIFolder *lpShortcutFolder, LPSPropValue lpPropSourceKey)
  131. {
  132. HRESULT hr = hrSuccess;
  133. object_ptr<IMAPITable> lpTable;
  134. rowset_ptr lpRows;
  135. memory_ptr<ENTRYLIST> lpsMsgList;
  136. static constexpr const SizedSPropTagArray(2, sPropDelFavo) =
  137. {2, {PR_ENTRYID, PR_FAV_PUBLIC_SOURCE_KEY}};
  138. std::list<string> listSourceKey;
  139. string strSourceKey;
  140. SPropValue sPropSourceKey;
  141. ULONG ulMaxRows = 0;
  142. if (lpShortcutFolder == nullptr || lpPropSourceKey == nullptr)
  143. return MAPI_E_INVALID_PARAMETER;
  144. hr = lpShortcutFolder->GetContentsTable(0, &~lpTable);
  145. if (hr != hrSuccess)
  146. return hr;
  147. hr = lpTable->GetRowCount(0, &ulMaxRows);
  148. if (hr != hrSuccess)
  149. return hr;
  150. hr = lpTable->SetColumns(sPropDelFavo, 0);
  151. if (hr != hrSuccess)
  152. return hr;
  153. // build restriction
  154. hr = ECPropertyRestriction(RELOP_EQ, PR_FAV_PUBLIC_SOURCE_KEY, lpPropSourceKey, ECRestriction::Cheap)
  155. .FindRowIn(lpTable, BOOKMARK_BEGINNING, 0);
  156. if (hr != hrSuccess)
  157. return hr; // Folder already removed (or memory problems)
  158. hr = lpTable->QueryRows (1, 0, &~lpRows);
  159. if (hr != hrSuccess)
  160. return hr;
  161. if (lpRows->cRows == 0)
  162. return hrSuccess; // Folder already removed
  163. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~lpsMsgList);
  164. if (hr != hrSuccess)
  165. return hr;
  166. hr = MAPIAllocateMore(sizeof(SBinary)*ulMaxRows, lpsMsgList, (void**)&lpsMsgList->lpbin);
  167. if (hr != hrSuccess)
  168. return hr;
  169. //FIXME: check the properties in the row!!!!
  170. lpsMsgList->cValues = 0;
  171. // add entryid
  172. lpsMsgList->lpbin[lpsMsgList->cValues].cb = lpRows->aRow[0].lpProps[0].Value.bin.cb;
  173. if ((hr = MAPIAllocateMore(lpsMsgList->lpbin[lpsMsgList->cValues].cb, lpsMsgList, (void **) &lpsMsgList->lpbin[lpsMsgList->cValues].lpb)) != hrSuccess)
  174. return hr;
  175. memcpy(lpsMsgList->lpbin[lpsMsgList->cValues].lpb, lpRows->aRow[0].lpProps[0].Value.bin.lpb, lpsMsgList->lpbin[lpsMsgList->cValues].cb);
  176. ++lpsMsgList->cValues;
  177. strSourceKey.assign((char*)lpRows->aRow[0].lpProps[1].Value.bin.lpb, lpRows->aRow[0].lpProps[1].Value.bin.cb);
  178. listSourceKey.push_back(strSourceKey);
  179. lpRows.reset();
  180. for (const auto &sk : listSourceKey) {
  181. sPropSourceKey.ulPropTag = PR_FAV_PUBLIC_SOURCE_KEY;
  182. sPropSourceKey.Value.bin.cb = sk.size();
  183. sPropSourceKey.Value.bin.lpb = const_cast<BYTE *>(reinterpret_cast<const BYTE *>(sk.c_str()));
  184. hr = ECPropertyRestriction(RELOP_EQ, PR_FAV_PARENT_SOURCE_KEY, &sPropSourceKey, ECRestriction::Cheap)
  185. .RestrictTable(lpTable);
  186. if (hr != hrSuccess)
  187. return hr;
  188. hr = lpTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
  189. if (hr != hrSuccess)
  190. return hr;
  191. while(true)
  192. {
  193. hr = lpTable->QueryRows(1, 0, &~lpRows);
  194. if (hr != hrSuccess)
  195. return hr;
  196. if (lpRows->cRows == 0)
  197. break; // no rows
  198. //FIXME: check the properties in the row!!!!
  199. // add entryid
  200. lpsMsgList->lpbin[lpsMsgList->cValues].cb = lpRows->aRow[0].lpProps[0].Value.bin.cb;
  201. if ((hr = MAPIAllocateMore(lpsMsgList->lpbin[lpsMsgList->cValues].cb, lpsMsgList, (void **) &lpsMsgList->lpbin[lpsMsgList->cValues].lpb)) != hrSuccess)
  202. return hr;
  203. memcpy(lpsMsgList->lpbin[lpsMsgList->cValues].lpb, lpRows->aRow[0].lpProps[0].Value.bin.lpb, lpsMsgList->lpbin[lpsMsgList->cValues].cb);
  204. ++lpsMsgList->cValues;
  205. // Add sourcekey into the list
  206. strSourceKey.assign((char*)lpRows->aRow[0].lpProps[1].Value.bin.lpb, lpRows->aRow[0].lpProps[1].Value.bin.cb);
  207. listSourceKey.push_back(strSourceKey);
  208. } //while(true)
  209. }
  210. return lpShortcutFolder->DeleteMessages(lpsMsgList, 0, nullptr, 0);
  211. }
  212. /**
  213. * Add a new favorite in the private store.
  214. *
  215. * @param[in] lpShortcutFolder The shortcut folder in the private store.
  216. * @param[in] ulLevel The depth of the folder, start from one.
  217. * @param[in] lpAliasName Alias name of the folder. Pass NULL to specify the standard foldername
  218. * @param[in] ulFlags possible MAPI_UNICODE for lpszAliasName
  219. * @param[in] cValues Count of property values pointed to by the lpPropArray parameter. The cValues parameter must not be zero.
  220. * @param[in] lpPropArray Pointer to an array of SPropValue structures holding property values to create the favorite.
  221. */
  222. HRESULT AddToFavorite(IMAPIFolder *lpShortcutFolder, ULONG ulLevel, LPCTSTR lpszAliasName, ULONG ulFlags, ULONG cValues, LPSPropValue lpPropArray)
  223. {
  224. HRESULT hr = hrSuccess;
  225. object_ptr<IMessage> lpMessage;
  226. object_ptr<IMAPITable> lpTable;
  227. memory_ptr<SPropValue> lpNewPropArray;
  228. ULONG cPropArray = 0;
  229. memory_ptr<SRestriction> lpRestriction;
  230. if (lpShortcutFolder == nullptr || lpPropArray == nullptr)
  231. return MAPI_E_INVALID_PARAMETER;
  232. auto lpPropSourceKey = PCpropFindProp(lpPropArray, cValues, PR_SOURCE_KEY);
  233. auto lpPropParentSourceKey = PCpropFindProp(lpPropArray, cValues, PR_PARENT_SOURCE_KEY);
  234. auto lpPropDisplayName = PCpropFindProp(lpPropArray, cValues, PR_DISPLAY_NAME);
  235. auto lpPropMessageClass = PCpropFindProp(lpPropArray, cValues, PR_CONTAINER_CLASS);
  236. if (lpPropSourceKey == NULL || lpPropParentSourceKey == NULL || lpPropDisplayName == NULL)
  237. return MAPI_E_CORRUPT_DATA;
  238. // Check for duplicates
  239. hr = lpShortcutFolder->GetContentsTable(0, &~lpTable);
  240. if (hr != hrSuccess)
  241. return hr;
  242. // build restriction
  243. hr = ECPropertyRestriction(RELOP_EQ, PR_FAV_PUBLIC_SOURCE_KEY, lpPropSourceKey, ECRestriction::Cheap)
  244. .CreateMAPIRestriction(&~lpRestriction, ECRestriction::Cheap);
  245. if (hr != hrSuccess)
  246. return hr;
  247. if (lpTable->FindRow(lpRestriction, BOOKMARK_BEGINNING , 0) == hrSuccess)
  248. return hrSuccess; /* Folder already included */
  249. // No duplicate, Start to add the favorite
  250. hr = lpShortcutFolder->CreateMessage(nullptr, 0, &~lpMessage);
  251. if (hr != hrSuccess)
  252. return hr;
  253. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 6, &~lpNewPropArray);
  254. if (hr != hrSuccess)
  255. return hr;
  256. lpNewPropArray[cPropArray].ulPropTag = PR_FAV_LEVEL_MASK;
  257. lpNewPropArray[cPropArray++].Value.ul = ulLevel;
  258. lpNewPropArray[cPropArray].ulPropTag = PR_FAV_PUBLIC_SOURCE_KEY;
  259. lpNewPropArray[cPropArray++].Value = lpPropSourceKey->Value;
  260. lpNewPropArray[cPropArray].ulPropTag = PR_FAV_DISPLAY_NAME;
  261. lpNewPropArray[cPropArray++].Value = lpPropDisplayName->Value;
  262. if (lpPropMessageClass) {
  263. lpNewPropArray[cPropArray].ulPropTag = PR_FAV_CONTAINER_CLASS;
  264. lpNewPropArray[cPropArray++].Value = lpPropMessageClass->Value;
  265. }
  266. if (ulLevel > 1) {
  267. lpNewPropArray[cPropArray].ulPropTag = PR_FAV_PARENT_SOURCE_KEY;
  268. lpNewPropArray[cPropArray++].Value = lpPropParentSourceKey->Value;
  269. }
  270. if (lpszAliasName && lpszAliasName[0] != '\0') {
  271. tstring tDisplay(lpPropDisplayName->Value.LPSZ);
  272. convstring csAlias(lpszAliasName, ulFlags);
  273. if ((std::wstring)csAlias != tDisplay)
  274. {
  275. lpNewPropArray[cPropArray].ulPropTag = (ulFlags & MAPI_UNICODE) ? PR_FAV_DISPLAY_ALIAS_W : PR_FAV_DISPLAY_ALIAS_A;
  276. lpNewPropArray[cPropArray++].Value.lpszA = (LPSTR)lpszAliasName;
  277. }
  278. }
  279. hr = lpMessage->SetProps(cPropArray, lpNewPropArray, NULL);
  280. if (hr != hrSuccess)
  281. return hr;
  282. return lpMessage->SaveChanges(0);
  283. }
  284. /**
  285. * Add new folders to the favorites folder
  286. *
  287. * @param lpSession Pointer to the current mapi session
  288. * @param lpFolder Pointer to a folder in the public store, except a folder from the favorites folder
  289. * @param lpAliasName Pointer to a string containing another name for the folder
  290. * @param ulFlags Bitmask of flags that controls how the folder is added. The following flags can be set:
  291. * FAVO_FOLDER_LEVEL_BASE
  292. * Add only the folder itself
  293. * FAVO_FOLDER_LEVEL_ONE
  294. * Add the folder and the immediate subfolders only
  295. * FAVO_FOLDER_LEVEL_SUB
  296. * Add the folder and all subfolders
  297. * MAPI_UNICODE
  298. * lpAliasName parameter is in wide or multibyte format
  299. */
  300. HRESULT AddFavoriteFolder(LPMAPIFOLDER lpShortcutFolder, LPMAPIFOLDER lpFolder, LPCTSTR lpAliasName, ULONG ulFlags)
  301. {
  302. HRESULT hr = hrSuccess;
  303. object_ptr<IMAPITable> lpTable;
  304. memory_ptr<SPropValue> lpsPropArray;
  305. ULONG ulFolderFlags = 0;
  306. ULONG cValues = 0;
  307. static constexpr const SizedSPropTagArray(5, sPropsFolderInfo) =
  308. {5, {PR_DEPTH, PR_SOURCE_KEY, PR_PARENT_SOURCE_KEY,
  309. PR_DISPLAY_NAME, PR_CONTAINER_CLASS}};
  310. // FIXME: check vaiables
  311. // Add folders to the shorcut folder
  312. hr = lpFolder->GetProps(sPropsFolderInfo, 0, &cValues, &~lpsPropArray);
  313. if (FAILED(hr) != hrSuccess) //Gives always a warning
  314. return hr;
  315. hr = AddToFavorite(lpShortcutFolder, 1, lpAliasName, ulFlags, cValues, lpsPropArray);
  316. if (hr != hrSuccess)
  317. return hr;
  318. if (ulFlags == FAVO_FOLDER_LEVEL_SUB)
  319. ulFolderFlags = CONVENIENT_DEPTH;
  320. else if (ulFlags == FAVO_FOLDER_LEVEL_ONE)
  321. ulFolderFlags = 0;
  322. else
  323. return hrSuccess; // Done
  324. // Get subfolders
  325. hr = lpFolder->GetHierarchyTable(ulFolderFlags, &~lpTable);
  326. if (hr != hrSuccess)
  327. return hr;
  328. hr = lpTable->SetColumns(sPropsFolderInfo, 0);
  329. if (hr != hrSuccess)
  330. return hr;
  331. // Add the favorite recusive depended what the flags are
  332. while(true)
  333. {
  334. rowset_ptr lpRows;
  335. hr = lpTable->QueryRows (1, 0, &~lpRows);
  336. if (hr != hrSuccess)
  337. return hr;
  338. if (lpRows->cRows == 0)
  339. break;
  340. auto lpPropDepth = PCpropFindProp(lpRows->aRow[0].lpProps,lpRows->aRow[0].cValues, PR_DEPTH);
  341. if (lpPropDepth == nullptr)
  342. return MAPI_E_CORRUPT_DATA; // Break the action
  343. hr = AddToFavorite(lpShortcutFolder, lpPropDepth->Value.ul + 1, NULL, 0, lpRows->aRow[0].cValues, lpRows->aRow[0].lpProps);
  344. if (hr != hrSuccess)
  345. // Break the action
  346. return hr;
  347. } //while(true)
  348. return hrSuccess;
  349. }
  350. } /* namespace */